From 8d032818f3bf541c296d9527b98beca3290af8cb Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Fri, 5 Jan 2024 13:00:27 -0500 Subject: [PATCH 01/15] Add a new command to check README entries --- .../lib/src/common/repository_package.dart | 5 + script/tool/lib/src/main.dart | 2 + .../src/repo_package_info_check_command.dart | 93 +++++++++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 script/tool/lib/src/repo_package_info_check_command.dart diff --git a/script/tool/lib/src/common/repository_package.dart b/script/tool/lib/src/common/repository_package.dart index 0ab33046d790..d677060712db 100644 --- a/script/tool/lib/src/common/repository_package.dart +++ b/script/tool/lib/src/common/repository_package.dart @@ -192,4 +192,9 @@ class RepositoryPackage { } return null; } + + /// Returns true if the package is not marked as "publish_to: none". + bool isPublishable() { + return parsePubspec().publishTo != 'none'; + } } diff --git a/script/tool/lib/src/main.dart b/script/tool/lib/src/main.dart index 89ffae268276..f2e08b0ec244 100644 --- a/script/tool/lib/src/main.dart +++ b/script/tool/lib/src/main.dart @@ -33,6 +33,7 @@ import 'publish_command.dart'; import 'pubspec_check_command.dart'; import 'readme_check_command.dart'; import 'remove_dev_dependencies_command.dart'; +import 'repo_package_info_check_command.dart'; import 'update_dependency_command.dart'; import 'update_excerpts_command.dart'; import 'update_min_sdk_command.dart'; @@ -82,6 +83,7 @@ void main(List args) { ..addCommand(PubspecCheckCommand(packagesDir)) ..addCommand(ReadmeCheckCommand(packagesDir)) ..addCommand(RemoveDevDependenciesCommand(packagesDir)) + ..addCommand(RepoPackageInfoCheckCommand(packagesDir)) ..addCommand(DartTestCommand(packagesDir)) ..addCommand(UpdateDependencyCommand(packagesDir)) ..addCommand(UpdateExcerptsCommand(packagesDir)) diff --git a/script/tool/lib/src/repo_package_info_check_command.dart b/script/tool/lib/src/repo_package_info_check_command.dart new file mode 100644 index 000000000000..cdd608b973e2 --- /dev/null +++ b/script/tool/lib/src/repo_package_info_check_command.dart @@ -0,0 +1,93 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:file/file.dart'; + +import 'common/core.dart'; +import 'common/output_utils.dart'; +import 'common/package_looping_command.dart'; +import 'common/repository_package.dart'; + +const int _exitBadTableEntry = 3; +const int _exitUknownPackageEntry = 4; + +/// A command to verify repository-level metadata about packages, such as +/// repo README and CODEOWNERS entries. +class RepoPackageInfoCheckCommand extends PackageLoopingCommand { + /// Creates Dependabot check command instance. + RepoPackageInfoCheckCommand(super.packagesDir, {super.gitDir}); + + late Directory _repoRoot; + + final Map> _readmeTableEntries = + >{}; + + @override + final String name = 'repo-package-info-check'; + + @override + List get aliases => ['check-repo-package-info']; + + @override + final String description = + 'Checks that all packages are listed correctly in the repo README.'; + + @override + final bool hasLongOutput = false; + + @override + Future initializeRun() async { + _repoRoot = packagesDir.fileSystem.directory((await gitDir).path); + + final RegExp namePattern = RegExp(r'\[(.*?)\]\('); + for (final String line + in _repoRoot.childFile('README.md').readAsLinesSync()) { + // Find all the table entries, skipping the header. + if (line.startsWith('|') && + !line.startsWith('| Package') && + !line.startsWith('|-')) { + final List cells = line + .split('|') + .map((String s) => s.trim()) + .where((String s) => s.isNotEmpty) + .toList(); + // Extract the name, removing any markdown escaping. + final String? name = + namePattern.firstMatch(cells[0])?.group(1)?.replaceAll(r'\_', '_'); + if (name == null) { + printError('Unexpected README table line:\n $line'); + throw ToolExit(_exitBadTableEntry); + } + _readmeTableEntries[name] = cells; + + if (!(packagesDir.childDirectory(name).existsSync() || + thirdPartyPackagesDir.childDirectory(name).existsSync())) { + printError('Uknown package "$name" in README table.'); + throw ToolExit(_exitUknownPackageEntry); + } + } + } + } + + @override + Future runForPackage(RepositoryPackage package) async { + final List errors = []; + + // All packages should have + + // Any published package should be in the README table. + // For federated plugins, only the app-facing package is listed. + if (package.isPublishable() && + (!package.isFederated || package.isAppFacing)) { + if (!_readmeTableEntries.containsKey(package.directory.basename)) { + printError('${indentation}Missing table entry'); + errors.add('Missing table entry'); + } + } + + return errors.isEmpty + ? PackageResult.success() + : PackageResult.fail(errors); + } +} From 6d2b8dff99175f1d652fec1a29f25057c2f2bd36 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Fri, 5 Jan 2024 13:19:29 -0500 Subject: [PATCH 02/15] Unit tests --- .../src/repo_package_info_check_command.dart | 6 +- .../repo_package_info_check_command_test.dart | 138 ++++++++++++++++++ 2 files changed, 141 insertions(+), 3 deletions(-) create mode 100644 script/tool/test/repo_package_info_check_command_test.dart diff --git a/script/tool/lib/src/repo_package_info_check_command.dart b/script/tool/lib/src/repo_package_info_check_command.dart index cdd608b973e2..d1eebfcba7f0 100644 --- a/script/tool/lib/src/repo_package_info_check_command.dart +++ b/script/tool/lib/src/repo_package_info_check_command.dart @@ -63,7 +63,7 @@ class RepoPackageInfoCheckCommand extends PackageLoopingCommand { if (!(packagesDir.childDirectory(name).existsSync() || thirdPartyPackagesDir.childDirectory(name).existsSync())) { - printError('Uknown package "$name" in README table.'); + printError('Unknown package "$name" in root README.md table.'); throw ToolExit(_exitUknownPackageEntry); } } @@ -81,8 +81,8 @@ class RepoPackageInfoCheckCommand extends PackageLoopingCommand { if (package.isPublishable() && (!package.isFederated || package.isAppFacing)) { if (!_readmeTableEntries.containsKey(package.directory.basename)) { - printError('${indentation}Missing table entry'); - errors.add('Missing table entry'); + printError('${indentation}Missing repo root README.md table entry'); + errors.add('Missing repo root README.md table entry'); } } diff --git a/script/tool/test/repo_package_info_check_command_test.dart b/script/tool/test/repo_package_info_check_command_test.dart new file mode 100644 index 000000000000..76495fc3d86c --- /dev/null +++ b/script/tool/test/repo_package_info_check_command_test.dart @@ -0,0 +1,138 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:args/command_runner.dart'; +import 'package:file/file.dart'; +import 'package:file/memory.dart'; +import 'package:flutter_plugin_tools/src/common/core.dart'; +import 'package:flutter_plugin_tools/src/repo_package_info_check_command.dart'; +import 'package:mockito/mockito.dart'; +import 'package:test/test.dart'; + +import 'common/package_command_test.mocks.dart'; +import 'util.dart'; + +void main() { + late CommandRunner runner; + late FileSystem fileSystem; + late Directory root; + late Directory packagesDir; + + setUp(() { + fileSystem = MemoryFileSystem(); + root = fileSystem.currentDirectory; + packagesDir = root.childDirectory('packages'); + + final MockGitDir gitDir = MockGitDir(); + when(gitDir.path).thenReturn(root.path); + + final RepoPackageInfoCheckCommand command = RepoPackageInfoCheckCommand( + packagesDir, + gitDir: gitDir, + ); + runner = CommandRunner( + 'dependabot_test', 'Test for $RepoPackageInfoCheckCommand'); + runner.addCommand(command); + }); + + String readmeTableHeader() { + return ''' +| Package | Pub | Points | Popularity | Issues | Pull requests | +|---------|-----|--------|------------|--------|---------------| +'''; + } + + String readmeTableEntry(String packageName, {String? tag}) { + final String encodedTag = Uri.encodeComponent(tag ?? 'p: $packageName'); + return '| [$packageName](./packages/$packageName/) | ' + '[![pub package](https://img.shields.io/pub/v/$packageName.svg)](https://pub.dev/packages/$packageName) | ' + '[![pub points](https://img.shields.io/pub/points/$packageName)](https://pub.dev/packages/$packageName/score) | ' + '[![popularity](https://img.shields.io/pub/popularity/$packageName)](https://pub.dev/packages/$packageName/score) | ' + '[![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/$encodedTag?label=)](https://github.com/flutter/flutter/labels/$encodedTag) | ' + '[![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/$encodedTag?label=)](https://github.com/flutter/packages/labels/$encodedTag) |'; + } + + test('passes for correct README coverage', () async { + createFakePackage('a_package', packagesDir); + + root.childFile('README.md').writeAsStringSync(''' +${readmeTableHeader()} +${readmeTableEntry('a_package')} +'''); + + final List output = + await runCapturingPrint(runner, ['repo-package-info-check']); + + expect(output, + containsAllInOrder([contains('Ran for 1 package(s)')])); + }); + + test('passes for federated plugins with only app-facing package listed', + () async { + const String pluginName = 'foo'; + final Directory pluginDir = packagesDir.childDirectory(pluginName); + createFakePlugin(pluginName, pluginDir); + createFakePlugin('${pluginName}_platform_interface', pluginDir); + createFakePlugin('${pluginName}_android', pluginDir); + createFakePlugin('${pluginName}_ios', pluginDir); + + root.childFile('README.md').writeAsStringSync(''' +${readmeTableHeader()} +${readmeTableEntry(pluginName)} +'''); + + final List output = + await runCapturingPrint(runner, ['repo-package-info-check']); + + expect(output, + containsAllInOrder([contains('Ran for 4 package(s)')])); + }); + + test('fails for unexpected README table entry', () async { + createFakePackage('a_package', packagesDir); + + root.childFile('README.md').writeAsStringSync(''' +${readmeTableHeader()} +${readmeTableEntry('another_package')} +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['repo-package-info-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('Unknown package "another_package" in root README.md table'), + ])); + }); + + test('fails for missing README table entry', () async { + createFakePackage('a_package', packagesDir); + createFakePackage('another_package', packagesDir); + + root.childFile('README.md').writeAsStringSync(''' +${readmeTableHeader()} +${readmeTableEntry('another_package')} +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['repo-package-info-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('Missing repo root README.md table entry'), + contains('a_package:\n' + ' Missing repo root README.md table entry') + ])); + }); +} From c24401f89616c7f97e720996a439f940d03dba08 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Fri, 5 Jan 2024 13:23:45 -0500 Subject: [PATCH 03/15] Add missing table entries --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index ce98d9b7d138..322c8b6d3a97 100644 --- a/README.md +++ b/README.md @@ -69,9 +69,11 @@ These are the packages hosted in this repository: | [plugin\_platform\_interface](./packages/plugin_platform_interface/) | [![pub package](https://img.shields.io/pub/v/plugin_platform_interface.svg)](https://pub.dev/packages/plugin_platform_interface) | [![pub points](https://img.shields.io/pub/points/plugin_platform_interface)](https://pub.dev/packages/plugin_platform_interface/score) | [![popularity](https://img.shields.io/pub/popularity/plugin_platform_interface)](https://pub.dev/packages/plugin_platform_interface/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20plugin_platform_interface?label=)](https://github.com/flutter/flutter/labels/p%3A%20plugin_platform_interface) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20plugin_platform_interface?label=)](https://github.com/flutter/packages/labels/p%3A%20plugin_platform_interface) | | [process](./packages/process/) | [![pub package](https://img.shields.io/pub/v/process.svg)](https://pub.dev/packages/process) | [![pub points](https://img.shields.io/pub/points/process)](https://pub.dev/packages/process/score) | [![popularity](https://img.shields.io/pub/popularity/process)](https://pub.dev/packages/process/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20process?label=)](https://github.com/flutter/flutter/labels/p%3A%20process) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20process?label=)](https://github.com/flutter/packages/labels/p%3A%20process) | | [quick\_actions](./packages/quick_actions/) | [![pub package](https://img.shields.io/pub/v/quick_actions.svg)](https://pub.dev/packages/quick_actions) | [![pub points](https://img.shields.io/pub/points/quick_actions)](https://pub.dev/packages/quick_actions/score) | [![popularity](https://img.shields.io/pub/popularity/quick_actions)](https://pub.dev/packages/quick_actions/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20quick_actions?label=)](https://github.com/flutter/flutter/labels/p%3A%20quick_actions) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20quick_actions?label=)](https://github.com/flutter/packages/labels/p%3A%20quick_actions) | +| [google\_identity\_services\_web](./packages/google_identity_services_web/) | [![pub package](https://img.shields.io/pub/v/google_identity_services_web.svg)](https://pub.dev/packages/google_identity_services_web) | [![pub points](https://img.shields.io/pub/points/google_identity_services_web)](https://pub.dev/packages/google_identity_services_web/score) | [![popularity](https://img.shields.io/pub/popularity/google_identity_services_web)](https://pub.dev/packages/google_identity_services_web/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20google_identity_services_web?label=)](https://github.com/flutter/flutter/labels/p%3A%20google_identity_services_web) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20google_identity_services_web?label=)](https://github.com/flutter/packages/labels/p%3A%20google_identity_services_web) | | [rfw](./packages/rfw/) | [![pub package](https://img.shields.io/pub/v/rfw.svg)](https://pub.dev/packages/rfw) | [![pub points](https://img.shields.io/pub/points/rfw)](https://pub.dev/packages/rfw/score) | [![popularity](https://img.shields.io/pub/popularity/rfw)](https://pub.dev/packages/rfw/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20rfw?label=)](https://github.com/flutter/flutter/labels/p%3A%20rfw) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20rfw?label=)](https://github.com/flutter/packages/labels/p%3A%20rfw) | | [shared\_preferences](./packages/shared_preferences/) | [![pub package](https://img.shields.io/pub/v/shared_preferences.svg)](https://pub.dev/packages/shared_preferences) | [![pub points](https://img.shields.io/pub/points/shared_preferences)](https://pub.dev/packages/shared_preferences/score) | [![popularity](https://img.shields.io/pub/popularity/shared_preferences)](https://pub.dev/packages/shared_preferences/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20shared_preferences?label=)](https://github.com/flutter/flutter/labels/p%3A%20shared_preferences) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20shared_preferences?label=)](https://github.com/flutter/packages/labels/p%3A%20shared_preferences) | | [standard\_message\_codec](./packages/standard_message_codec/) | [![pub package](https://img.shields.io/pub/v/standard_message_codec.svg)](https://pub.dev/packages/standard_message_codec) | [![pub points](https://img.shields.io/pub/points/standard_message_codec)](https://pub.dev/packages/standard_message_codec/score) | [![popularity](https://img.shields.io/pub/popularity/standard_message_codec)](https://pub.dev/packages/standard_message_codec/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20standard_message_codec?label=)](https://github.com/flutter/flutter/labels/p%3A%20standard_message_codec) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20standard_message_codec?label=)](https://github.com/flutter/packages/labels/p%3A%20standard_message_codec) | +| [two\_dimensional\_scrollables](./packages/two_dimensional_scrollables/) | [![pub package](https://img.shields.io/pub/v/two_dimensional_scrollables.svg)](https://pub.dev/packages/two_dimensional_scrollables) | [![pub points](https://img.shields.io/pub/points/two_dimensional_scrollables)](https://pub.dev/packages/two_dimensional_scrollables/score) | [![popularity](https://img.shields.io/pub/popularity/two_dimensional_scrollables)](https://pub.dev/packages/two_dimensional_scrollables/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20two_dimensional_scrollables?label=)](https://github.com/flutter/flutter/labels/p%3A%20two_dimensional_scrollables) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20two_dimensional_scrollables?label=)](https://github.com/flutter/packages/labels/p%3A%20two_dimensional_scrollables) | | [url\_launcher](./packages/url_launcher/) | [![pub package](https://img.shields.io/pub/v/url_launcher.svg)](https://pub.dev/packages/url_launcher) | [![pub points](https://img.shields.io/pub/points/url_launcher)](https://pub.dev/packages/url_launcher/score) | [![popularity](https://img.shields.io/pub/popularity/url_launcher)](https://pub.dev/packages/url_launcher/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20url_launcher?label=)](https://github.com/flutter/flutter/labels/p%3A%20url_launcher) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20url_launcher?label=)](https://github.com/flutter/packages/labels/p%3A%20url_launcher) | | [video\_player](./packages/video_player/) | [![pub package](https://img.shields.io/pub/v/video_player.svg)](https://pub.dev/packages/video_player) | [![pub points](https://img.shields.io/pub/points/video_player)](https://pub.dev/packages/video_player/score) | [![popularity](https://img.shields.io/pub/popularity/video_player)](https://pub.dev/packages/video_player/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20video_player?label=)](https://github.com/flutter/flutter/labels/p%3A%20video_player) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20video_player?label=)](https://github.com/flutter/packages/labels/p%3A%20video_player) | | [web\_benchmarks](./packages/web_benchmarks/) | [![pub package](https://img.shields.io/pub/v/web_benchmarks.svg)](https://pub.dev/packages/web_benchmarks) | [![pub points](https://img.shields.io/pub/points/web_benchmarks)](https://pub.dev/packages/web_benchmarks/score) | [![popularity](https://img.shields.io/pub/popularity/web_benchmarks)](https://pub.dev/packages/web_benchmarks/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20web_benchmarks?label=)](https://github.com/flutter/flutter/labels/p%3A%20web_benchmarks) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20web_benchmarks?label=)](https://github.com/flutter/packages/labels/p%3A%20web_benchmarks) | From 3ac3f369cb35f019104c5bd0adb0850a06ed9431 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Fri, 5 Jan 2024 13:56:01 -0500 Subject: [PATCH 04/15] Add link validation --- .../src/repo_package_info_check_command.dart | 36 +++++- .../repo_package_info_check_command_test.dart | 104 ++++++++++++++++++ 2 files changed, 139 insertions(+), 1 deletion(-) diff --git a/script/tool/lib/src/repo_package_info_check_command.dart b/script/tool/lib/src/repo_package_info_check_command.dart index d1eebfcba7f0..42aa70e099a5 100644 --- a/script/tool/lib/src/repo_package_info_check_command.dart +++ b/script/tool/lib/src/repo_package_info_check_command.dart @@ -80,9 +80,43 @@ class RepoPackageInfoCheckCommand extends PackageLoopingCommand { // For federated plugins, only the app-facing package is listed. if (package.isPublishable() && (!package.isFederated || package.isAppFacing)) { - if (!_readmeTableEntries.containsKey(package.directory.basename)) { + final String packageName = package.directory.basename; + final List? cells = _readmeTableEntries[packageName]; + + if (cells == null) { printError('${indentation}Missing repo root README.md table entry'); errors.add('Missing repo root README.md table entry'); + } else { + // Extract the two parts of a "[label](link)" .md link. + final RegExp mdLinkPattern = RegExp(r'\[().*\]\((.*)\)'); + // Possible link targets. + for (final String cell in cells) { + final RegExpMatch? match = mdLinkPattern.firstMatch(cell); + if (match == null) { + printError( + '${indentation}Invalid repo root README.md table entry: "$cell"'); + errors.add('Invalid root README.md table entry'); + } else { + final String encodedTag = Uri.encodeComponent('p: $packageName'); + final String text = match.group(1)!; + final String target = match.group(2)!; + // A link should be one of: + // - a relative link to the in-repo package + // - a pub.dev link to the package + // - a github label link to the package's label. + final RegExp pubDevLink = + RegExp('^https://pub.dev/packages/$packageName(?:/score)?\$'); + final RegExp gitHubLink = RegExp( + '^https://github.com/flutter/(?:flutter|packages)/labels/$encodedTag\$'); + if (!(target == './packages/$packageName/' || + pubDevLink.hasMatch(target) || + gitHubLink.hasMatch(target))) { + printError( + '${indentation}Incorrect link in root README.md table: "$target"'); + errors.add('Incorrect link in root README.md table'); + } + } + } } } diff --git a/script/tool/test/repo_package_info_check_command_test.dart b/script/tool/test/repo_package_info_check_command_test.dart index 76495fc3d86c..545624455542 100644 --- a/script/tool/test/repo_package_info_check_command_test.dart +++ b/script/tool/test/repo_package_info_check_command_test.dart @@ -135,4 +135,108 @@ ${readmeTableEntry('another_package')} ' Missing repo root README.md table entry') ])); }); + + test('fails for unexpected format in README table entry', () async { + const String packageName = 'a_package'; + final String encodedTag = Uri.encodeComponent(packageName); + createFakePackage('a_package', packagesDir); + + final String entry = '| [$packageName](./packages/$packageName/) | ' + 'Some random text | ' + '[![pub points](https://img.shields.io/pub/points/$packageName)](https://pub.dev/packages/$packageName/score) | ' + '[![popularity](https://img.shields.io/pub/popularity/$packageName)](https://pub.dev/packages/$packageName/score) | ' + '[![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/$encodedTag?label=)](https://github.com/flutter/flutter/labels/$encodedTag) | ' + '[![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/$encodedTag?label=)](https://github.com/flutter/packages/labels/$encodedTag) |'; + + root.childFile('README.md').writeAsStringSync(''' +${readmeTableHeader()} +$entry +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['repo-package-info-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains( + 'Invalid repo root README.md table entry: "Some random text"'), + contains('a_package:\n' + ' Invalid root README.md table entry') + ])); + }); + + test('fails for incorrect packages/* link in README table entry', () async { + const String packageName = 'a_package'; + final String encodedTag = Uri.encodeComponent(packageName); + const String incorrectPackageName = 'a_pakage'; + createFakePackage('a_package', packagesDir); + + final String entry = '| [$packageName](./packages/$packageName/) | ' + '[![pub package](https://img.shields.io/pub/v/$packageName.svg)](https://pub.dev/packages/$packageName) | ' + '[![pub points](https://img.shields.io/pub/points/$packageName)](https://pub.dev/packages/$incorrectPackageName/score) | ' + '[![popularity](https://img.shields.io/pub/popularity/$packageName)](https://pub.dev/packages/$packageName/score) | ' + '[![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/$encodedTag?label=)](https://github.com/flutter/flutter/labels/$encodedTag) | ' + '[![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/$encodedTag?label=)](https://github.com/flutter/packages/labels/$encodedTag) |'; + + root.childFile('README.md').writeAsStringSync(''' +${readmeTableHeader()} +$entry +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['repo-package-info-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains( + 'Incorrect link in root README.md table: "https://pub.dev/packages/$incorrectPackageName/score"'), + contains('a_package:\n' + ' Incorrect link in root README.md table') + ])); + }); + + test('fails for incorrect labels/* link in README table entry', () async { + const String packageName = 'a_package'; + final String encodedTag = Uri.encodeComponent(packageName); + final String incorrectTag = Uri.encodeComponent('a_pakage'); + createFakePackage('a_package', packagesDir); + + final String entry = '| [$packageName](./packages/$packageName/) | ' + '[![pub package](https://img.shields.io/pub/v/$packageName.svg)](https://pub.dev/packages/$packageName) | ' + '[![pub points](https://img.shields.io/pub/points/$packageName)](https://pub.dev/packages/$packageName/score) | ' + '[![popularity](https://img.shields.io/pub/popularity/$packageName)](https://pub.dev/packages/$packageName/score) | ' + '[![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/$encodedTag?label=)](https://github.com/flutter/flutter/labels/$incorrectTag) | ' + '[![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/$encodedTag?label=)](https://github.com/flutter/packages/labels/$encodedTag) |'; + + root.childFile('README.md').writeAsStringSync(''' +${readmeTableHeader()} +$entry +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['repo-package-info-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains( + 'Incorrect link in root README.md table: "https://github.com/flutter/flutter/labels/$incorrectTag"'), + contains('a_package:\n' + ' Incorrect link in root README.md table') + ])); + }); } From c76b3d28797b7e2d4625c90bb36a48156b490255 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Fri, 5 Jan 2024 14:21:40 -0500 Subject: [PATCH 05/15] Validate table entries --- .../src/repo_package_info_check_command.dart | 30 ++++- .../repo_package_info_check_command_test.dart | 114 +++++++++++++++++- 2 files changed, 136 insertions(+), 8 deletions(-) diff --git a/script/tool/lib/src/repo_package_info_check_command.dart b/script/tool/lib/src/repo_package_info_check_command.dart index 42aa70e099a5..ff83781b0100 100644 --- a/script/tool/lib/src/repo_package_info_check_command.dart +++ b/script/tool/lib/src/repo_package_info_check_command.dart @@ -88,7 +88,7 @@ class RepoPackageInfoCheckCommand extends PackageLoopingCommand { errors.add('Missing repo root README.md table entry'); } else { // Extract the two parts of a "[label](link)" .md link. - final RegExp mdLinkPattern = RegExp(r'\[().*\]\((.*)\)'); + final RegExp mdLinkPattern = RegExp(r'^\[(.*)\]\((.*)\)$'); // Possible link targets. for (final String cell in cells) { final RegExpMatch? match = mdLinkPattern.firstMatch(cell); @@ -98,12 +98,34 @@ class RepoPackageInfoCheckCommand extends PackageLoopingCommand { errors.add('Invalid root README.md table entry'); } else { final String encodedTag = Uri.encodeComponent('p: $packageName'); - final String text = match.group(1)!; + final String anchor = match.group(1)!; final String target = match.group(2)!; - // A link should be one of: + + // The anchor should be one of: + // - The package name (optionally with any underscores escaped) + // - An image with a name-based link + // - An image with a tag-based link + final RegExp packageLink = + RegExp(r'^!\[.*\]\(https://img.shields.io/pub/.*/' + '$packageName' + r'(?:\.svg)?\)$'); + final RegExp tagLink = + RegExp(r'^!\[.*\]\(https://img.shields.io/github/.*/' + '$encodedTag' + r'\?label=\)$'); + if (!(anchor == packageName || + anchor == packageName.replaceAll('_', r'\_') || + packageLink.hasMatch(anchor) || + tagLink.hasMatch(anchor))) { + printError( + '${indentation}Incorrect anchor in root README.md table: "$anchor"'); + errors.add('Incorrect anchor in root README.md table'); + } + + // The link should be one of: // - a relative link to the in-repo package // - a pub.dev link to the package - // - a github label link to the package's label. + // - a github label link to the package's label final RegExp pubDevLink = RegExp('^https://pub.dev/packages/$packageName(?:/score)?\$'); final RegExp gitHubLink = RegExp( diff --git a/script/tool/test/repo_package_info_check_command_test.dart b/script/tool/test/repo_package_info_check_command_test.dart index 545624455542..16d165d17b34 100644 --- a/script/tool/test/repo_package_info_check_command_test.dart +++ b/script/tool/test/repo_package_info_check_command_test.dart @@ -138,7 +138,7 @@ ${readmeTableEntry('another_package')} test('fails for unexpected format in README table entry', () async { const String packageName = 'a_package'; - final String encodedTag = Uri.encodeComponent(packageName); + final String encodedTag = Uri.encodeComponent('p: $packageName'); createFakePackage('a_package', packagesDir); final String entry = '| [$packageName](./packages/$packageName/) | ' @@ -170,9 +170,45 @@ $entry ])); }); + test('fails for incorrect source link in README table entry', () async { + const String packageName = 'a_package'; + final String encodedTag = Uri.encodeComponent('p: $packageName'); + const String incorrectPackageName = 'a_pakage'; + createFakePackage('a_package', packagesDir); + + final String entry = + '| [$packageName](./packages/$incorrectPackageName/) | ' + '[![pub package](https://img.shields.io/pub/v/$packageName.svg)](https://pub.dev/packages/$packageName) | ' + '[![pub points](https://img.shields.io/pub/points/$packageName)](https://pub.dev/packages/$packageName/score) | ' + '[![popularity](https://img.shields.io/pub/popularity/$packageName)](https://pub.dev/packages/$packageName/score) | ' + '[![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/$encodedTag?label=)](https://github.com/flutter/flutter/labels/$encodedTag) | ' + '[![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/$encodedTag?label=)](https://github.com/flutter/packages/labels/$encodedTag) |'; + + root.childFile('README.md').writeAsStringSync(''' +${readmeTableHeader()} +$entry +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['repo-package-info-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains( + 'Incorrect link in root README.md table: "./packages/$incorrectPackageName/"'), + contains('a_package:\n' + ' Incorrect link in root README.md table') + ])); + }); + test('fails for incorrect packages/* link in README table entry', () async { const String packageName = 'a_package'; - final String encodedTag = Uri.encodeComponent(packageName); + final String encodedTag = Uri.encodeComponent('p: $packageName'); const String incorrectPackageName = 'a_pakage'; createFakePackage('a_package', packagesDir); @@ -207,8 +243,8 @@ $entry test('fails for incorrect labels/* link in README table entry', () async { const String packageName = 'a_package'; - final String encodedTag = Uri.encodeComponent(packageName); - final String incorrectTag = Uri.encodeComponent('a_pakage'); + final String encodedTag = Uri.encodeComponent('p: $packageName'); + final String incorrectTag = Uri.encodeComponent('p: a_pakage'); createFakePackage('a_package', packagesDir); final String entry = '| [$packageName](./packages/$packageName/) | ' @@ -239,4 +275,74 @@ $entry ' Incorrect link in root README.md table') ])); }); + + test('fails for incorrect packages/* anchor in README table entry', () async { + const String packageName = 'a_package'; + final String encodedTag = Uri.encodeComponent('p: $packageName'); + const String incorrectPackageName = 'a_pakage'; + createFakePackage('a_package', packagesDir); + + final String entry = '| [$packageName](./packages/$packageName/) | ' + '[![pub package](https://img.shields.io/pub/v/$packageName.svg)](https://pub.dev/packages/$packageName) | ' + '[![pub points](https://img.shields.io/pub/points/$incorrectPackageName)](https://pub.dev/packages/$packageName/score) | ' + '[![popularity](https://img.shields.io/pub/popularity/$packageName)](https://pub.dev/packages/$packageName/score) | ' + '[![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/$encodedTag?label=)](https://github.com/flutter/flutter/labels/$encodedTag) | ' + '[![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/$encodedTag?label=)](https://github.com/flutter/packages/labels/$encodedTag) |'; + + root.childFile('README.md').writeAsStringSync(''' +${readmeTableHeader()} +$entry +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['repo-package-info-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains( + 'Incorrect anchor in root README.md table: "![pub points](https://img.shields.io/pub/points/$incorrectPackageName)"'), + contains('a_package:\n' + ' Incorrect anchor in root README.md table') + ])); + }); + + test('fails for incorrect tag query anchor in README table entry', () async { + const String packageName = 'a_package'; + final String encodedTag = Uri.encodeComponent('p: $packageName'); + final String incorrectTag = Uri.encodeComponent('p: a_pakage'); + createFakePackage('a_package', packagesDir); + + final String entry = '| [$packageName](./packages/$packageName/) | ' + '[![pub package](https://img.shields.io/pub/v/$packageName.svg)](https://pub.dev/packages/$packageName) | ' + '[![pub points](https://img.shields.io/pub/points/$packageName)](https://pub.dev/packages/$packageName/score) | ' + '[![popularity](https://img.shields.io/pub/popularity/$packageName)](https://pub.dev/packages/$packageName/score) | ' + '[![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/$incorrectTag?label=)](https://github.com/flutter/flutter/labels/$encodedTag) | ' + '[![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/$encodedTag?label=)](https://github.com/flutter/packages/labels/$encodedTag) |'; + + root.childFile('README.md').writeAsStringSync(''' +${readmeTableHeader()} +$entry +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['repo-package-info-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains( + 'Incorrect anchor in root README.md table: "![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/$incorrectTag?label=)'), + contains('a_package:\n' + ' Incorrect anchor in root README.md table') + ])); + }); } From f21320d31d1ec0aa2619fcb3243c652eec8b6bdf Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Fri, 5 Jan 2024 14:26:20 -0500 Subject: [PATCH 06/15] Escape :s in table --- README.md | 84 +++++++++++++++++++++++++++---------------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 322c8b6d3a97..40321e57d624 100644 --- a/README.md +++ b/README.md @@ -37,45 +37,45 @@ These are the packages hosted in this repository: | Package | Pub | Points | Popularity | Issues | Pull requests | |---------|-----|--------|------------|--------|---------------| -| [animations](./packages/animations/) | [![pub package](https://img.shields.io/pub/v/animations.svg)](https://pub.dev/packages/animations) | [![pub points](https://img.shields.io/pub/points/animations)](https://pub.dev/packages/animations/score) | [![popularity](https://img.shields.io/pub/popularity/animations)](https://pub.dev/packages/animations/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20animations?label=)](https://github.com/flutter/flutter/labels/p%3A%20animations) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20animations?label=)](https://github.com/flutter/packages/labels/p%3A%20animations) | -| [camera](./packages/camera/) | [![pub package](https://img.shields.io/pub/v/camera.svg)](https://pub.dev/packages/camera) | [![pub points](https://img.shields.io/pub/points/camera)](https://pub.dev/packages/camera/score) | [![popularity](https://img.shields.io/pub/popularity/camera)](https://pub.dev/packages/camera/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20camera?label=)](https://github.com/flutter/flutter/labels/p%3A%20camera) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20camera?label=)](https://github.com/flutter/packages/labels/p%3A%20camera) | -| [cross\_file](./packages/cross_file/) | [![pub package](https://img.shields.io/pub/v/cross_file.svg)](https://pub.dev/packages/cross_file) | [![pub points](https://img.shields.io/pub/points/cross_file)](https://pub.dev/packages/cross_file/score) | [![popularity](https://img.shields.io/pub/popularity/cross_file)](https://pub.dev/packages/cross_file/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20cross_file?label=)](https://github.com/flutter/flutter/labels/p%3A%20cross_file) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20cross_file?label=)](https://github.com/flutter/packages/labels/p%3A%20cross_file) | -| [css\_colors](./packages/css_colors/) | [![pub package](https://img.shields.io/pub/v/css_colors.svg)](https://pub.dev/packages/css_colors) | [![pub points](https://img.shields.io/pub/points/css_colors)](https://pub.dev/packages/css_colors/score) | [![popularity](https://img.shields.io/pub/popularity/css_colors)](https://pub.dev/packages/css_colors/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20css_colors?label=)](https://github.com/flutter/flutter/labels/p%3A%20css_colors) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20css_colors?label=)](https://github.com/flutter/packages/labels/p%3A%20css_colors) | -| [cupertino\_icons](./third_party/packages/cupertino_icons/) | [![pub package](https://img.shields.io/pub/v/cupertino_icons.svg)](https://pub.dev/packages/cupertino_icons) | [![pub points](https://img.shields.io/pub/points/cupertino_icons)](https://pub.dev/packages/cupertino_icons/score) | [![popularity](https://img.shields.io/pub/popularity/cupertino_icons)](https://pub.dev/packages/cupertino_icons/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20cupertino_icons?label=)](https://github.com/flutter/flutter/labels/p%3A%20cupertino_icons) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20cupertino_icons?label=)](https://github.com/flutter/packages/labels/p%3A%20cupertino_icons) | -| [espresso](./packages/espresso/) | [![pub package](https://img.shields.io/pub/v/espresso.svg)](https://pub.dev/packages/espresso) | [![pub points](https://img.shields.io/pub/points/espresso)](https://pub.dev/packages/espresso/score) | [![popularity](https://img.shields.io/pub/popularity/espresso)](https://pub.dev/packages/espresso/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20espresso?label=)](https://github.com/flutter/flutter/labels/p%3A%20espresso) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20espresso?label=)](https://github.com/flutter/packages/labels/p%3A%20espresso) | -| [extension\_google\_sign\_in\_as\_googleapis\_auth](./packages/extension_google_sign_in_as_googleapis_auth/) | [![pub package](https://img.shields.io/pub/v/extension_google_sign_in_as_googleapis_auth.svg)](https://pub.dev/packages/extension_google_sign_in_as_googleapis_auth) | [![pub points](https://img.shields.io/pub/points/extension_google_sign_in_as_googleapis_auth)](https://pub.dev/packages/extension_google_sign_in_as_googleapis_auth/score) | [![popularity](https://img.shields.io/pub/popularity/extension_google_sign_in_as_googleapis_auth)](https://pub.dev/packages/extension_google_sign_in_as_googleapis_auth/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20extension_google_sign_in_as_googleapis_auth?label=)](https://github.com/flutter/flutter/labels/p%3A%20extension_google_sign_in_as_googleapis_auth) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20extension_google_sign_in_as_googleapis_auth?label=)](https://github.com/flutter/packages/labels/p%3A%20extension_google_sign_in_as_googleapis_auth) | -| [file\_selector](./packages/file_selector/) | [![pub package](https://img.shields.io/pub/v/file_selector.svg)](https://pub.dev/packages/file_selector) | [![pub points](https://img.shields.io/pub/points/file_selector)](https://pub.dev/packages/file_selector/score) | [![popularity](https://img.shields.io/pub/popularity/file_selector)](https://pub.dev/packages/file_selector/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20file_selector?label=)](https://github.com/flutter/flutter/labels/p%3A%20file_selector) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20file_selector?label=)](https://github.com/flutter/packages/labels/p%3A%20file_selector) | -| [flutter\_adaptive\_scaffold](./packages/flutter_adaptive_scaffold/) | [![pub package](https://img.shields.io/pub/v/flutter_adaptive_scaffold.svg)](https://pub.dev/packages/flutter_adaptive_scaffold) | [![pub points](https://img.shields.io/pub/points/flutter_adaptive_scaffold)](https://pub.dev/packages/flutter_adaptive_scaffold/score) | [![popularity](https://img.shields.io/pub/popularity/flutter_adaptive_scaffold)](https://pub.dev/packages/flutter_adaptive_scaffold/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20flutter_adaptive_scaffold?label=)](https://github.com/flutter/flutter/labels/p%3A%20flutter_adaptive_scaffold) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20flutter_adaptive_scaffold?label=)](https://github.com/flutter/packages/labels/p%3A%20flutter_adaptive_scaffold) | -| [flutter\_image](./packages/flutter_image/) | [![pub package](https://img.shields.io/pub/v/flutter_image.svg)](https://pub.dev/packages/flutter_image) | [![pub points](https://img.shields.io/pub/points/flutter_image)](https://pub.dev/packages/flutter_image/score) | [![popularity](https://img.shields.io/pub/popularity/flutter_image)](https://pub.dev/packages/flutter_image/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20flutter_image?label=)](https://github.com/flutter/flutter/labels/p%3A%20flutter_image) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20flutter_image?label=)](https://github.com/flutter/packages/labels/p%3A%20flutter_image) | -| [flutter\_lints](./packages/flutter_lints/) | [![pub package](https://img.shields.io/pub/v/flutter_lints.svg)](https://pub.dev/packages/flutter_lints) | [![pub points](https://img.shields.io/pub/points/flutter_lints)](https://pub.dev/packages/flutter_lints/score) | [![popularity](https://img.shields.io/pub/popularity/flutter_lints)](https://pub.dev/packages/flutter_lints/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20flutter_lints?label=)](https://github.com/flutter/flutter/labels/p%3A%20flutter_lints) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20flutter_lints?label=)](https://github.com/flutter/packages/labels/p%3A%20flutter_lints) | -| [flutter\_markdown](./packages/flutter_markdown/) | [![pub package](https://img.shields.io/pub/v/flutter_markdown.svg)](https://pub.dev/packages/flutter_markdown) | [![pub points](https://img.shields.io/pub/points/flutter_markdown)](https://pub.dev/packages/flutter_markdown/score) | [![popularity](https://img.shields.io/pub/popularity/flutter_markdown)](https://pub.dev/packages/flutter_markdown/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20flutter_markdown?label=)](https://github.com/flutter/flutter/labels/p%3A%20flutter_markdown) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20flutter_markdown?label=)](https://github.com/flutter/packages/labels/p%3A%20flutter_markdown) | -| [flutter\_plugin\_android\_lifecycle](./packages/flutter_plugin_android_lifecycle/) | [![pub package](https://img.shields.io/pub/v/flutter_plugin_android_lifecycle.svg)](https://pub.dev/packages/flutter_plugin_android_lifecycle) | [![pub points](https://img.shields.io/pub/points/flutter_plugin_android_lifecycle)](https://pub.dev/packages/flutter_plugin_android_lifecycle/score) | [![popularity](https://img.shields.io/pub/popularity/flutter_plugin_android_lifecycle)](https://pub.dev/packages/flutter_plugin_android_lifecycle/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20flutter_plugin_android_lifecycle?label=)](https://github.com/flutter/flutter/labels/p%3A%20flutter_plugin_android_lifecycle) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20flutter_plugin_android_lifecycle?label=)](https://github.com/flutter/packages/labels/p%3A%20flutter_plugin_android_lifecycle) | -| [flutter\_template\_images](./packages/flutter_template_images/) | [![pub package](https://img.shields.io/pub/v/flutter_template_images.svg)](https://pub.dev/packages/flutter_template_images) | [![pub points](https://img.shields.io/pub/points/flutter_template_images)](https://pub.dev/packages/flutter_template_images/score) | [![popularity](https://img.shields.io/pub/popularity/flutter_template_images)](https://pub.dev/packages/flutter_template_images/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20flutter_template_images?label=)](https://github.com/flutter/flutter/labels/p%3A%20flutter_template_images) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20flutter_template_images?label=)](https://github.com/flutter/packages/labels/p%3A%20flutter_template_images) | -| [go\_router](./packages/go_router/) | [![pub package](https://img.shields.io/pub/v/go_router.svg)](https://pub.dev/packages/go_router) | [![pub points](https://img.shields.io/pub/points/go_router)](https://pub.dev/packages/go_router/score) | [![popularity](https://img.shields.io/pub/popularity/go_router)](https://pub.dev/packages/go_router/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20go_router?label=)](https://github.com/flutter/flutter/labels/p%3A%20go_router) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20go_router?label=)](https://github.com/flutter/packages/labels/p%3A%20go_router) | -| [go\_router\_builder](./packages/go_router_builder/) | [![pub package](https://img.shields.io/pub/v/go_router_builder.svg)](https://pub.dev/packages/go_router_builder) | [![pub points](https://img.shields.io/pub/points/go_router_builder)](https://pub.dev/packages/go_router_builder/score) | [![popularity](https://img.shields.io/pub/popularity/go_router_builder)](https://pub.dev/packages/go_router_builder/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20go_router_builder?label=)](https://github.com/flutter/flutter/labels/p%3A%20go_router_builder) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20go_router_builder?label=)](https://github.com/flutter/packages/labels/p%3A%20go_router_builder) | -| [google\_maps\_flutter](./packages/google_maps_flutter) | [![pub package](https://img.shields.io/pub/v/google_maps_flutter.svg)](https://pub.dev/packages/google_maps_flutter) | [![pub points](https://img.shields.io/pub/points/google_maps_flutter)](https://pub.dev/packages/google_maps_flutter/score) | [![popularity](https://img.shields.io/pub/popularity/google_maps_flutter)](https://pub.dev/packages/google_maps_flutter/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20maps?label=)](https://github.com/flutter/flutter/labels/p%3A%20maps) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20google_maps_flutter?label=)](https://github.com/flutter/packages/labels/p%3A%20google_maps_flutter) | -| [google\_sign\_in](./packages/google_sign_in/) | [![pub package](https://img.shields.io/pub/v/google_sign_in.svg)](https://pub.dev/packages/google_sign_in) | [![pub points](https://img.shields.io/pub/points/google_sign_in)](https://pub.dev/packages/google_sign_in/score) | [![popularity](https://img.shields.io/pub/popularity/google_sign_in)](https://pub.dev/packages/google_sign_in/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20google_sign_in?label=)](https://github.com/flutter/flutter/labels/p%3A%20google_sign_in) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20google_sign_in?label=)](https://github.com/flutter/packages/labels/p%3A%20google_sign_in) | -| [image\_picker](./packages/image_picker/) | [![pub package](https://img.shields.io/pub/v/image_picker.svg)](https://pub.dev/packages/image_picker) | [![pub points](https://img.shields.io/pub/points/image_picker)](https://pub.dev/packages/image_picker/score) | [![popularity](https://img.shields.io/pub/popularity/image_picker)](https://pub.dev/packages/image_picker/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20image_picker?label=)](https://github.com/flutter/flutter/labels/p%3A%20image_picker) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20image_picker?label=)](https://github.com/flutter/packages/labels/p%3A%20image_picker) | -| [in\_app\_purchase](./packages/in_app_purchase/) | [![pub package](https://img.shields.io/pub/v/in_app_purchase.svg)](https://pub.dev/packages/in_app_purchase) | [![pub points](https://img.shields.io/pub/points/in_app_purchase)](https://pub.dev/packages/in_app_purchase/score) | [![popularity](https://img.shields.io/pub/popularity/in_app_purchase)](https://pub.dev/packages/in_app_purchase/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20in_app_purchase?label=)](https://github.com/flutter/flutter/labels/p%3A%20in_app_purchase) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20in_app_purchase?label=)](https://github.com/flutter/packages/labels/p%3A%20in_app_purchase) | -| [ios\_platform\_images](./packages/ios_platform_images/) | [![pub package](https://img.shields.io/pub/v/ios_platform_images.svg)](https://pub.dev/packages/ios_platform_images) | [![pub points](https://img.shields.io/pub/points/ios_platform_images)](https://pub.dev/packages/ios_platform_images/score) | [![popularity](https://img.shields.io/pub/popularity/ios_platform_images)](https://pub.dev/packages/ios_platform_images/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20ios_platform_images?label=)](https://github.com/flutter/flutter/labels/p%3A%20ios_platform_images) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20ios_platform_images?label=)](https://github.com/flutter/packages/labels/p%3A%20ios_platform_images) | -| [local\_auth](./packages/local_auth/) | [![pub package](https://img.shields.io/pub/v/local_auth.svg)](https://pub.dev/packages/local_auth) | [![pub points](https://img.shields.io/pub/points/local_auth)](https://pub.dev/packages/local_auth/score) | [![popularity](https://img.shields.io/pub/popularity/local_auth)](https://pub.dev/packages/local_auth/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20local_auth?label=)](https://github.com/flutter/flutter/labels/p%3A%20local_auth) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20local_auth?label=)](https://github.com/flutter/packages/labels/p%3A%20local_auth) | -| [metrics\_center](./packages/metrics_center/) | [![pub package](https://img.shields.io/pub/v/metrics_center.svg)](https://pub.dev/packages/metrics_center) | [![pub points](https://img.shields.io/pub/points/metrics_center)](https://pub.dev/packages/metrics_center/score) | [![popularity](https://img.shields.io/pub/popularity/metrics_center)](https://pub.dev/packages/metrics_center/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20metrics_center?label=)](https://github.com/flutter/flutter/labels/p%3A%20metrics_center) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20metrics_center?label=)](https://github.com/flutter/packages/labels/p%3A%20metrics_center) | -| [multicast\_dns](./packages/multicast_dns/) | [![pub package](https://img.shields.io/pub/v/multicast_dns.svg)](https://pub.dev/packages/multicast_dns) | [![pub points](https://img.shields.io/pub/points/multicast_dns)](https://pub.dev/packages/multicast_dns/score) | [![popularity](https://img.shields.io/pub/popularity/multicast_dns)](https://pub.dev/packages/multicast_dns/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20multicast_dns?label=)](https://github.com/flutter/flutter/labels/p%3A%20multicast_dns) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20multicast_dns?label=)](https://github.com/flutter/packages/labels/p%3A%20multicast_dns) | -| [palette\_generator](./packages/palette_generator/) | [![pub package](https://img.shields.io/pub/v/palette_generator.svg)](https://pub.dartlang.org/packages/palette_generator) | [![pub points](https://img.shields.io/pub/points/palette_generator)](https://pub.dev/packages/palette_generator/score) | [![popularity](https://img.shields.io/pub/popularity/palette_generator)](https://pub.dev/packages/palette_generator/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20palette_generator?label=)](https://github.com/flutter/flutter/labels/p%3A%20palette_generator) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20palette_generator?label=)](https://github.com/flutter/packages/labels/p%3A%20palette_generator) | -| [path\_provider](./packages/path_provider/) | [![pub package](https://img.shields.io/pub/v/path_provider.svg)](https://pub.dev/packages/path_provider) | [![pub points](https://img.shields.io/pub/points/path_provider)](https://pub.dev/packages/path_provider/score) | [![popularity](https://img.shields.io/pub/popularity/path_provider)](https://pub.dev/packages/path_provider/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20path_provider?label=)](https://github.com/flutter/flutter/labels/p%3A%20path_provider) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20path_provider?label=)](https://github.com/flutter/packages/labels/p%3A%20path_provider) | -| [pigeon](./packages/pigeon/) | [![pub package](https://img.shields.io/pub/v/pigeon.svg)](https://pub.dev/packages/pigeon) | [![pub points](https://img.shields.io/pub/points/pigeon)](https://pub.dev/packages/pigeon/score) | [![popularity](https://img.shields.io/pub/popularity/pigeon)](https://pub.dev/packages/pigeon/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/pigeon?label=)](https://github.com/flutter/flutter/labels/pigeon) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20pigeon?label=)](https://github.com/flutter/packages/labels/p%3A%20pigeon) | -| [platform](./packages/platform/) | [![pub package](https://img.shields.io/pub/v/platform.svg)](https://pub.dev/packages/platform) | [![pub points](https://img.shields.io/pub/points/platform)](https://pub.dev/packages/platform/score) | [![popularity](https://img.shields.io/pub/popularity/platform)](https://pub.dev/packages/platform/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20platform?label=)](https://github.com/flutter/flutter/labels/p%3A%20platform) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20platform?label=)](https://github.com/flutter/packages/labels/p%3A%20platform) | -| [pointer\_interceptor](./packages/pointer_interceptor/) | [![pub package](https://img.shields.io/pub/v/pointer_interceptor.svg)](https://pub.dev/packages/pointer_interceptor) | [![pub points](https://img.shields.io/pub/points/pointer_interceptor)](https://pub.dev/packages/pointer_interceptor/score) | [![popularity](https://img.shields.io/pub/popularity/pointer_interceptor)](https://pub.dev/packages/pointer_interceptor/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20pointer_interceptor?label=)](https://github.com/flutter/flutter/labels/p%3A%20pointer_interceptor) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20pointer_interceptor?label=)](https://github.com/flutter/packages/labels/p%3A%20pointer_interceptor) | -| [plugin\_platform\_interface](./packages/plugin_platform_interface/) | [![pub package](https://img.shields.io/pub/v/plugin_platform_interface.svg)](https://pub.dev/packages/plugin_platform_interface) | [![pub points](https://img.shields.io/pub/points/plugin_platform_interface)](https://pub.dev/packages/plugin_platform_interface/score) | [![popularity](https://img.shields.io/pub/popularity/plugin_platform_interface)](https://pub.dev/packages/plugin_platform_interface/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20plugin_platform_interface?label=)](https://github.com/flutter/flutter/labels/p%3A%20plugin_platform_interface) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20plugin_platform_interface?label=)](https://github.com/flutter/packages/labels/p%3A%20plugin_platform_interface) | -| [process](./packages/process/) | [![pub package](https://img.shields.io/pub/v/process.svg)](https://pub.dev/packages/process) | [![pub points](https://img.shields.io/pub/points/process)](https://pub.dev/packages/process/score) | [![popularity](https://img.shields.io/pub/popularity/process)](https://pub.dev/packages/process/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20process?label=)](https://github.com/flutter/flutter/labels/p%3A%20process) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20process?label=)](https://github.com/flutter/packages/labels/p%3A%20process) | -| [quick\_actions](./packages/quick_actions/) | [![pub package](https://img.shields.io/pub/v/quick_actions.svg)](https://pub.dev/packages/quick_actions) | [![pub points](https://img.shields.io/pub/points/quick_actions)](https://pub.dev/packages/quick_actions/score) | [![popularity](https://img.shields.io/pub/popularity/quick_actions)](https://pub.dev/packages/quick_actions/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20quick_actions?label=)](https://github.com/flutter/flutter/labels/p%3A%20quick_actions) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20quick_actions?label=)](https://github.com/flutter/packages/labels/p%3A%20quick_actions) | -| [google\_identity\_services\_web](./packages/google_identity_services_web/) | [![pub package](https://img.shields.io/pub/v/google_identity_services_web.svg)](https://pub.dev/packages/google_identity_services_web) | [![pub points](https://img.shields.io/pub/points/google_identity_services_web)](https://pub.dev/packages/google_identity_services_web/score) | [![popularity](https://img.shields.io/pub/popularity/google_identity_services_web)](https://pub.dev/packages/google_identity_services_web/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20google_identity_services_web?label=)](https://github.com/flutter/flutter/labels/p%3A%20google_identity_services_web) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20google_identity_services_web?label=)](https://github.com/flutter/packages/labels/p%3A%20google_identity_services_web) | -| [rfw](./packages/rfw/) | [![pub package](https://img.shields.io/pub/v/rfw.svg)](https://pub.dev/packages/rfw) | [![pub points](https://img.shields.io/pub/points/rfw)](https://pub.dev/packages/rfw/score) | [![popularity](https://img.shields.io/pub/popularity/rfw)](https://pub.dev/packages/rfw/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20rfw?label=)](https://github.com/flutter/flutter/labels/p%3A%20rfw) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20rfw?label=)](https://github.com/flutter/packages/labels/p%3A%20rfw) | -| [shared\_preferences](./packages/shared_preferences/) | [![pub package](https://img.shields.io/pub/v/shared_preferences.svg)](https://pub.dev/packages/shared_preferences) | [![pub points](https://img.shields.io/pub/points/shared_preferences)](https://pub.dev/packages/shared_preferences/score) | [![popularity](https://img.shields.io/pub/popularity/shared_preferences)](https://pub.dev/packages/shared_preferences/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20shared_preferences?label=)](https://github.com/flutter/flutter/labels/p%3A%20shared_preferences) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20shared_preferences?label=)](https://github.com/flutter/packages/labels/p%3A%20shared_preferences) | -| [standard\_message\_codec](./packages/standard_message_codec/) | [![pub package](https://img.shields.io/pub/v/standard_message_codec.svg)](https://pub.dev/packages/standard_message_codec) | [![pub points](https://img.shields.io/pub/points/standard_message_codec)](https://pub.dev/packages/standard_message_codec/score) | [![popularity](https://img.shields.io/pub/popularity/standard_message_codec)](https://pub.dev/packages/standard_message_codec/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20standard_message_codec?label=)](https://github.com/flutter/flutter/labels/p%3A%20standard_message_codec) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20standard_message_codec?label=)](https://github.com/flutter/packages/labels/p%3A%20standard_message_codec) | -| [two\_dimensional\_scrollables](./packages/two_dimensional_scrollables/) | [![pub package](https://img.shields.io/pub/v/two_dimensional_scrollables.svg)](https://pub.dev/packages/two_dimensional_scrollables) | [![pub points](https://img.shields.io/pub/points/two_dimensional_scrollables)](https://pub.dev/packages/two_dimensional_scrollables/score) | [![popularity](https://img.shields.io/pub/popularity/two_dimensional_scrollables)](https://pub.dev/packages/two_dimensional_scrollables/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20two_dimensional_scrollables?label=)](https://github.com/flutter/flutter/labels/p%3A%20two_dimensional_scrollables) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20two_dimensional_scrollables?label=)](https://github.com/flutter/packages/labels/p%3A%20two_dimensional_scrollables) | -| [url\_launcher](./packages/url_launcher/) | [![pub package](https://img.shields.io/pub/v/url_launcher.svg)](https://pub.dev/packages/url_launcher) | [![pub points](https://img.shields.io/pub/points/url_launcher)](https://pub.dev/packages/url_launcher/score) | [![popularity](https://img.shields.io/pub/popularity/url_launcher)](https://pub.dev/packages/url_launcher/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20url_launcher?label=)](https://github.com/flutter/flutter/labels/p%3A%20url_launcher) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20url_launcher?label=)](https://github.com/flutter/packages/labels/p%3A%20url_launcher) | -| [video\_player](./packages/video_player/) | [![pub package](https://img.shields.io/pub/v/video_player.svg)](https://pub.dev/packages/video_player) | [![pub points](https://img.shields.io/pub/points/video_player)](https://pub.dev/packages/video_player/score) | [![popularity](https://img.shields.io/pub/popularity/video_player)](https://pub.dev/packages/video_player/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20video_player?label=)](https://github.com/flutter/flutter/labels/p%3A%20video_player) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20video_player?label=)](https://github.com/flutter/packages/labels/p%3A%20video_player) | -| [web\_benchmarks](./packages/web_benchmarks/) | [![pub package](https://img.shields.io/pub/v/web_benchmarks.svg)](https://pub.dev/packages/web_benchmarks) | [![pub points](https://img.shields.io/pub/points/web_benchmarks)](https://pub.dev/packages/web_benchmarks/score) | [![popularity](https://img.shields.io/pub/popularity/web_benchmarks)](https://pub.dev/packages/web_benchmarks/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20web_benchmarks?label=)](https://github.com/flutter/flutter/labels/p%3A%20web_benchmarks) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20web_benchmarks?label=)](https://github.com/flutter/packages/labels/p%3A%20web_benchmarks) | -| [webview\_flutter](./packages/webview_flutter/) | [![pub package](https://img.shields.io/pub/v/webview_flutter.svg)](https://pub.dev/packages/webview_flutter) | [![pub points](https://img.shields.io/pub/points/webview_flutter)](https://pub.dev/packages/webview_flutter/score) | [![popularity](https://img.shields.io/pub/popularity/webview_flutter)](https://pub.dev/packages/webview_flutter/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20webview?label=)](https://github.com/flutter/flutter/labels/p%3A%20webview) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20webview_flutter?label=)](https://github.com/flutter/packages/labels/p%3A%20webview_flutter) | -| [xdg\_directories](./packages/xdg_directories/) | [![pub package](https://img.shields.io/pub/v/xdg_directories.svg)](https://pub.dev/packages/xdg_directories) | [![pub points](https://img.shields.io/pub/points/xdg_directories)](https://pub.dev/packages/xdg_directories/score) | [![popularity](https://img.shields.io/pub/popularity/xdg_directories)](https://pub.dev/packages/xdg_directories/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20xdg_directories?label=)](https://github.com/flutter/flutter/labels/p%3A%20xdg_directories) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p:%20xdg_directories?label=)](https://github.com/flutter/packages/labels/p%3A%20xdg_directories) | +| [animations](./packages/animations/) | [![pub package](https://img.shields.io/pub/v/animations.svg)](https://pub.dev/packages/animations) | [![pub points](https://img.shields.io/pub/points/animations)](https://pub.dev/packages/animations/score) | [![popularity](https://img.shields.io/pub/popularity/animations)](https://pub.dev/packages/animations/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20animations?label=)](https://github.com/flutter/flutter/labels/p%3A%20animations) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20animations?label=)](https://github.com/flutter/packages/labels/p%3A%20animations) | +| [camera](./packages/camera/) | [![pub package](https://img.shields.io/pub/v/camera.svg)](https://pub.dev/packages/camera) | [![pub points](https://img.shields.io/pub/points/camera)](https://pub.dev/packages/camera/score) | [![popularity](https://img.shields.io/pub/popularity/camera)](https://pub.dev/packages/camera/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20camera?label=)](https://github.com/flutter/flutter/labels/p%3A%20camera) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20camera?label=)](https://github.com/flutter/packages/labels/p%3A%20camera) | +| [cross\_file](./packages/cross_file/) | [![pub package](https://img.shields.io/pub/v/cross_file.svg)](https://pub.dev/packages/cross_file) | [![pub points](https://img.shields.io/pub/points/cross_file)](https://pub.dev/packages/cross_file/score) | [![popularity](https://img.shields.io/pub/popularity/cross_file)](https://pub.dev/packages/cross_file/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20cross_file?label=)](https://github.com/flutter/flutter/labels/p%3A%20cross_file) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20cross_file?label=)](https://github.com/flutter/packages/labels/p%3A%20cross_file) | +| [css\_colors](./packages/css_colors/) | [![pub package](https://img.shields.io/pub/v/css_colors.svg)](https://pub.dev/packages/css_colors) | [![pub points](https://img.shields.io/pub/points/css_colors)](https://pub.dev/packages/css_colors/score) | [![popularity](https://img.shields.io/pub/popularity/css_colors)](https://pub.dev/packages/css_colors/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20css_colors?label=)](https://github.com/flutter/flutter/labels/p%3A%20css_colors) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20css_colors?label=)](https://github.com/flutter/packages/labels/p%3A%20css_colors) | +| [cupertino\_icons](./third_party/packages/cupertino_icons/) | [![pub package](https://img.shields.io/pub/v/cupertino_icons.svg)](https://pub.dev/packages/cupertino_icons) | [![pub points](https://img.shields.io/pub/points/cupertino_icons)](https://pub.dev/packages/cupertino_icons/score) | [![popularity](https://img.shields.io/pub/popularity/cupertino_icons)](https://pub.dev/packages/cupertino_icons/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20cupertino_icons?label=)](https://github.com/flutter/flutter/labels/p%3A%20cupertino_icons) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20cupertino_icons?label=)](https://github.com/flutter/packages/labels/p%3A%20cupertino_icons) | +| [espresso](./packages/espresso/) | [![pub package](https://img.shields.io/pub/v/espresso.svg)](https://pub.dev/packages/espresso) | [![pub points](https://img.shields.io/pub/points/espresso)](https://pub.dev/packages/espresso/score) | [![popularity](https://img.shields.io/pub/popularity/espresso)](https://pub.dev/packages/espresso/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20espresso?label=)](https://github.com/flutter/flutter/labels/p%3A%20espresso) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20espresso?label=)](https://github.com/flutter/packages/labels/p%3A%20espresso) | +| [extension\_google\_sign\_in\_as\_googleapis\_auth](./packages/extension_google_sign_in_as_googleapis_auth/) | [![pub package](https://img.shields.io/pub/v/extension_google_sign_in_as_googleapis_auth.svg)](https://pub.dev/packages/extension_google_sign_in_as_googleapis_auth) | [![pub points](https://img.shields.io/pub/points/extension_google_sign_in_as_googleapis_auth)](https://pub.dev/packages/extension_google_sign_in_as_googleapis_auth/score) | [![popularity](https://img.shields.io/pub/popularity/extension_google_sign_in_as_googleapis_auth)](https://pub.dev/packages/extension_google_sign_in_as_googleapis_auth/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20extension_google_sign_in_as_googleapis_auth?label=)](https://github.com/flutter/flutter/labels/p%3A%20extension_google_sign_in_as_googleapis_auth) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20extension_google_sign_in_as_googleapis_auth?label=)](https://github.com/flutter/packages/labels/p%3A%20extension_google_sign_in_as_googleapis_auth) | +| [file\_selector](./packages/file_selector/) | [![pub package](https://img.shields.io/pub/v/file_selector.svg)](https://pub.dev/packages/file_selector) | [![pub points](https://img.shields.io/pub/points/file_selector)](https://pub.dev/packages/file_selector/score) | [![popularity](https://img.shields.io/pub/popularity/file_selector)](https://pub.dev/packages/file_selector/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20file_selector?label=)](https://github.com/flutter/flutter/labels/p%3A%20file_selector) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20file_selector?label=)](https://github.com/flutter/packages/labels/p%3A%20file_selector) | +| [flutter\_adaptive\_scaffold](./packages/flutter_adaptive_scaffold/) | [![pub package](https://img.shields.io/pub/v/flutter_adaptive_scaffold.svg)](https://pub.dev/packages/flutter_adaptive_scaffold) | [![pub points](https://img.shields.io/pub/points/flutter_adaptive_scaffold)](https://pub.dev/packages/flutter_adaptive_scaffold/score) | [![popularity](https://img.shields.io/pub/popularity/flutter_adaptive_scaffold)](https://pub.dev/packages/flutter_adaptive_scaffold/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20flutter_adaptive_scaffold?label=)](https://github.com/flutter/flutter/labels/p%3A%20flutter_adaptive_scaffold) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20flutter_adaptive_scaffold?label=)](https://github.com/flutter/packages/labels/p%3A%20flutter_adaptive_scaffold) | +| [flutter\_image](./packages/flutter_image/) | [![pub package](https://img.shields.io/pub/v/flutter_image.svg)](https://pub.dev/packages/flutter_image) | [![pub points](https://img.shields.io/pub/points/flutter_image)](https://pub.dev/packages/flutter_image/score) | [![popularity](https://img.shields.io/pub/popularity/flutter_image)](https://pub.dev/packages/flutter_image/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20flutter_image?label=)](https://github.com/flutter/flutter/labels/p%3A%20flutter_image) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20flutter_image?label=)](https://github.com/flutter/packages/labels/p%3A%20flutter_image) | +| [flutter\_lints](./packages/flutter_lints/) | [![pub package](https://img.shields.io/pub/v/flutter_lints.svg)](https://pub.dev/packages/flutter_lints) | [![pub points](https://img.shields.io/pub/points/flutter_lints)](https://pub.dev/packages/flutter_lints/score) | [![popularity](https://img.shields.io/pub/popularity/flutter_lints)](https://pub.dev/packages/flutter_lints/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20flutter_lints?label=)](https://github.com/flutter/flutter/labels/p%3A%20flutter_lints) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20flutter_lints?label=)](https://github.com/flutter/packages/labels/p%3A%20flutter_lints) | +| [flutter\_markdown](./packages/flutter_markdown/) | [![pub package](https://img.shields.io/pub/v/flutter_markdown.svg)](https://pub.dev/packages/flutter_markdown) | [![pub points](https://img.shields.io/pub/points/flutter_markdown)](https://pub.dev/packages/flutter_markdown/score) | [![popularity](https://img.shields.io/pub/popularity/flutter_markdown)](https://pub.dev/packages/flutter_markdown/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20flutter_markdown?label=)](https://github.com/flutter/flutter/labels/p%3A%20flutter_markdown) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20flutter_markdown?label=)](https://github.com/flutter/packages/labels/p%3A%20flutter_markdown) | +| [flutter\_plugin\_android\_lifecycle](./packages/flutter_plugin_android_lifecycle/) | [![pub package](https://img.shields.io/pub/v/flutter_plugin_android_lifecycle.svg)](https://pub.dev/packages/flutter_plugin_android_lifecycle) | [![pub points](https://img.shields.io/pub/points/flutter_plugin_android_lifecycle)](https://pub.dev/packages/flutter_plugin_android_lifecycle/score) | [![popularity](https://img.shields.io/pub/popularity/flutter_plugin_android_lifecycle)](https://pub.dev/packages/flutter_plugin_android_lifecycle/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20flutter_plugin_android_lifecycle?label=)](https://github.com/flutter/flutter/labels/p%3A%20flutter_plugin_android_lifecycle) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20flutter_plugin_android_lifecycle?label=)](https://github.com/flutter/packages/labels/p%3A%20flutter_plugin_android_lifecycle) | +| [flutter\_template\_images](./packages/flutter_template_images/) | [![pub package](https://img.shields.io/pub/v/flutter_template_images.svg)](https://pub.dev/packages/flutter_template_images) | [![pub points](https://img.shields.io/pub/points/flutter_template_images)](https://pub.dev/packages/flutter_template_images/score) | [![popularity](https://img.shields.io/pub/popularity/flutter_template_images)](https://pub.dev/packages/flutter_template_images/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20flutter_template_images?label=)](https://github.com/flutter/flutter/labels/p%3A%20flutter_template_images) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20flutter_template_images?label=)](https://github.com/flutter/packages/labels/p%3A%20flutter_template_images) | +| [go\_router](./packages/go_router/) | [![pub package](https://img.shields.io/pub/v/go_router.svg)](https://pub.dev/packages/go_router) | [![pub points](https://img.shields.io/pub/points/go_router)](https://pub.dev/packages/go_router/score) | [![popularity](https://img.shields.io/pub/popularity/go_router)](https://pub.dev/packages/go_router/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20go_router?label=)](https://github.com/flutter/flutter/labels/p%3A%20go_router) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20go_router?label=)](https://github.com/flutter/packages/labels/p%3A%20go_router) | +| [go\_router\_builder](./packages/go_router_builder/) | [![pub package](https://img.shields.io/pub/v/go_router_builder.svg)](https://pub.dev/packages/go_router_builder) | [![pub points](https://img.shields.io/pub/points/go_router_builder)](https://pub.dev/packages/go_router_builder/score) | [![popularity](https://img.shields.io/pub/popularity/go_router_builder)](https://pub.dev/packages/go_router_builder/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20go_router_builder?label=)](https://github.com/flutter/flutter/labels/p%3A%20go_router_builder) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20go_router_builder?label=)](https://github.com/flutter/packages/labels/p%3A%20go_router_builder) | +| [google\_maps\_flutter](./packages/google_maps_flutter) | [![pub package](https://img.shields.io/pub/v/google_maps_flutter.svg)](https://pub.dev/packages/google_maps_flutter) | [![pub points](https://img.shields.io/pub/points/google_maps_flutter)](https://pub.dev/packages/google_maps_flutter/score) | [![popularity](https://img.shields.io/pub/popularity/google_maps_flutter)](https://pub.dev/packages/google_maps_flutter/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20maps?label=)](https://github.com/flutter/flutter/labels/p%3A%20maps) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20google_maps_flutter?label=)](https://github.com/flutter/packages/labels/p%3A%20google_maps_flutter) | +| [google\_sign\_in](./packages/google_sign_in/) | [![pub package](https://img.shields.io/pub/v/google_sign_in.svg)](https://pub.dev/packages/google_sign_in) | [![pub points](https://img.shields.io/pub/points/google_sign_in)](https://pub.dev/packages/google_sign_in/score) | [![popularity](https://img.shields.io/pub/popularity/google_sign_in)](https://pub.dev/packages/google_sign_in/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20google_sign_in?label=)](https://github.com/flutter/flutter/labels/p%3A%20google_sign_in) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20google_sign_in?label=)](https://github.com/flutter/packages/labels/p%3A%20google_sign_in) | +| [image\_picker](./packages/image_picker/) | [![pub package](https://img.shields.io/pub/v/image_picker.svg)](https://pub.dev/packages/image_picker) | [![pub points](https://img.shields.io/pub/points/image_picker)](https://pub.dev/packages/image_picker/score) | [![popularity](https://img.shields.io/pub/popularity/image_picker)](https://pub.dev/packages/image_picker/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20image_picker?label=)](https://github.com/flutter/flutter/labels/p%3A%20image_picker) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20image_picker?label=)](https://github.com/flutter/packages/labels/p%3A%20image_picker) | +| [in\_app\_purchase](./packages/in_app_purchase/) | [![pub package](https://img.shields.io/pub/v/in_app_purchase.svg)](https://pub.dev/packages/in_app_purchase) | [![pub points](https://img.shields.io/pub/points/in_app_purchase)](https://pub.dev/packages/in_app_purchase/score) | [![popularity](https://img.shields.io/pub/popularity/in_app_purchase)](https://pub.dev/packages/in_app_purchase/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20in_app_purchase?label=)](https://github.com/flutter/flutter/labels/p%3A%20in_app_purchase) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20in_app_purchase?label=)](https://github.com/flutter/packages/labels/p%3A%20in_app_purchase) | +| [ios\_platform\_images](./packages/ios_platform_images/) | [![pub package](https://img.shields.io/pub/v/ios_platform_images.svg)](https://pub.dev/packages/ios_platform_images) | [![pub points](https://img.shields.io/pub/points/ios_platform_images)](https://pub.dev/packages/ios_platform_images/score) | [![popularity](https://img.shields.io/pub/popularity/ios_platform_images)](https://pub.dev/packages/ios_platform_images/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20ios_platform_images?label=)](https://github.com/flutter/flutter/labels/p%3A%20ios_platform_images) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20ios_platform_images?label=)](https://github.com/flutter/packages/labels/p%3A%20ios_platform_images) | +| [local\_auth](./packages/local_auth/) | [![pub package](https://img.shields.io/pub/v/local_auth.svg)](https://pub.dev/packages/local_auth) | [![pub points](https://img.shields.io/pub/points/local_auth)](https://pub.dev/packages/local_auth/score) | [![popularity](https://img.shields.io/pub/popularity/local_auth)](https://pub.dev/packages/local_auth/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20local_auth?label=)](https://github.com/flutter/flutter/labels/p%3A%20local_auth) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20local_auth?label=)](https://github.com/flutter/packages/labels/p%3A%20local_auth) | +| [metrics\_center](./packages/metrics_center/) | [![pub package](https://img.shields.io/pub/v/metrics_center.svg)](https://pub.dev/packages/metrics_center) | [![pub points](https://img.shields.io/pub/points/metrics_center)](https://pub.dev/packages/metrics_center/score) | [![popularity](https://img.shields.io/pub/popularity/metrics_center)](https://pub.dev/packages/metrics_center/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20metrics_center?label=)](https://github.com/flutter/flutter/labels/p%3A%20metrics_center) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20metrics_center?label=)](https://github.com/flutter/packages/labels/p%3A%20metrics_center) | +| [multicast\_dns](./packages/multicast_dns/) | [![pub package](https://img.shields.io/pub/v/multicast_dns.svg)](https://pub.dev/packages/multicast_dns) | [![pub points](https://img.shields.io/pub/points/multicast_dns)](https://pub.dev/packages/multicast_dns/score) | [![popularity](https://img.shields.io/pub/popularity/multicast_dns)](https://pub.dev/packages/multicast_dns/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20multicast_dns?label=)](https://github.com/flutter/flutter/labels/p%3A%20multicast_dns) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20multicast_dns?label=)](https://github.com/flutter/packages/labels/p%3A%20multicast_dns) | +| [palette\_generator](./packages/palette_generator/) | [![pub package](https://img.shields.io/pub/v/palette_generator.svg)](https://pub.dartlang.org/packages/palette_generator) | [![pub points](https://img.shields.io/pub/points/palette_generator)](https://pub.dev/packages/palette_generator/score) | [![popularity](https://img.shields.io/pub/popularity/palette_generator)](https://pub.dev/packages/palette_generator/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20palette_generator?label=)](https://github.com/flutter/flutter/labels/p%3A%20palette_generator) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20palette_generator?label=)](https://github.com/flutter/packages/labels/p%3A%20palette_generator) | +| [path\_provider](./packages/path_provider/) | [![pub package](https://img.shields.io/pub/v/path_provider.svg)](https://pub.dev/packages/path_provider) | [![pub points](https://img.shields.io/pub/points/path_provider)](https://pub.dev/packages/path_provider/score) | [![popularity](https://img.shields.io/pub/popularity/path_provider)](https://pub.dev/packages/path_provider/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20path_provider?label=)](https://github.com/flutter/flutter/labels/p%3A%20path_provider) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20path_provider?label=)](https://github.com/flutter/packages/labels/p%3A%20path_provider) | +| [pigeon](./packages/pigeon/) | [![pub package](https://img.shields.io/pub/v/pigeon.svg)](https://pub.dev/packages/pigeon) | [![pub points](https://img.shields.io/pub/points/pigeon)](https://pub.dev/packages/pigeon/score) | [![popularity](https://img.shields.io/pub/popularity/pigeon)](https://pub.dev/packages/pigeon/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/pigeon?label=)](https://github.com/flutter/flutter/labels/pigeon) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20pigeon?label=)](https://github.com/flutter/packages/labels/p%3A%20pigeon) | +| [platform](./packages/platform/) | [![pub package](https://img.shields.io/pub/v/platform.svg)](https://pub.dev/packages/platform) | [![pub points](https://img.shields.io/pub/points/platform)](https://pub.dev/packages/platform/score) | [![popularity](https://img.shields.io/pub/popularity/platform)](https://pub.dev/packages/platform/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20platform?label=)](https://github.com/flutter/flutter/labels/p%3A%20platform) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20platform?label=)](https://github.com/flutter/packages/labels/p%3A%20platform) | +| [pointer\_interceptor](./packages/pointer_interceptor/) | [![pub package](https://img.shields.io/pub/v/pointer_interceptor.svg)](https://pub.dev/packages/pointer_interceptor) | [![pub points](https://img.shields.io/pub/points/pointer_interceptor)](https://pub.dev/packages/pointer_interceptor/score) | [![popularity](https://img.shields.io/pub/popularity/pointer_interceptor)](https://pub.dev/packages/pointer_interceptor/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20pointer_interceptor?label=)](https://github.com/flutter/flutter/labels/p%3A%20pointer_interceptor) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20pointer_interceptor?label=)](https://github.com/flutter/packages/labels/p%3A%20pointer_interceptor) | +| [plugin\_platform\_interface](./packages/plugin_platform_interface/) | [![pub package](https://img.shields.io/pub/v/plugin_platform_interface.svg)](https://pub.dev/packages/plugin_platform_interface) | [![pub points](https://img.shields.io/pub/points/plugin_platform_interface)](https://pub.dev/packages/plugin_platform_interface/score) | [![popularity](https://img.shields.io/pub/popularity/plugin_platform_interface)](https://pub.dev/packages/plugin_platform_interface/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20plugin_platform_interface?label=)](https://github.com/flutter/flutter/labels/p%3A%20plugin_platform_interface) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20plugin_platform_interface?label=)](https://github.com/flutter/packages/labels/p%3A%20plugin_platform_interface) | +| [process](./packages/process/) | [![pub package](https://img.shields.io/pub/v/process.svg)](https://pub.dev/packages/process) | [![pub points](https://img.shields.io/pub/points/process)](https://pub.dev/packages/process/score) | [![popularity](https://img.shields.io/pub/popularity/process)](https://pub.dev/packages/process/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20process?label=)](https://github.com/flutter/flutter/labels/p%3A%20process) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20process?label=)](https://github.com/flutter/packages/labels/p%3A%20process) | +| [quick\_actions](./packages/quick_actions/) | [![pub package](https://img.shields.io/pub/v/quick_actions.svg)](https://pub.dev/packages/quick_actions) | [![pub points](https://img.shields.io/pub/points/quick_actions)](https://pub.dev/packages/quick_actions/score) | [![popularity](https://img.shields.io/pub/popularity/quick_actions)](https://pub.dev/packages/quick_actions/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20quick_actions?label=)](https://github.com/flutter/flutter/labels/p%3A%20quick_actions) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20quick_actions?label=)](https://github.com/flutter/packages/labels/p%3A%20quick_actions) | +| [google\_identity\_services\_web](./packages/google_identity_services_web/) | [![pub package](https://img.shields.io/pub/v/google_identity_services_web.svg)](https://pub.dev/packages/google_identity_services_web) | [![pub points](https://img.shields.io/pub/points/google_identity_services_web)](https://pub.dev/packages/google_identity_services_web/score) | [![popularity](https://img.shields.io/pub/popularity/google_identity_services_web)](https://pub.dev/packages/google_identity_services_web/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20google_identity_services_web?label=)](https://github.com/flutter/flutter/labels/p%3A%20google_identity_services_web) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20google_identity_services_web?label=)](https://github.com/flutter/packages/labels/p%3A%20google_identity_services_web) | +| [rfw](./packages/rfw/) | [![pub package](https://img.shields.io/pub/v/rfw.svg)](https://pub.dev/packages/rfw) | [![pub points](https://img.shields.io/pub/points/rfw)](https://pub.dev/packages/rfw/score) | [![popularity](https://img.shields.io/pub/popularity/rfw)](https://pub.dev/packages/rfw/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20rfw?label=)](https://github.com/flutter/flutter/labels/p%3A%20rfw) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20rfw?label=)](https://github.com/flutter/packages/labels/p%3A%20rfw) | +| [shared\_preferences](./packages/shared_preferences/) | [![pub package](https://img.shields.io/pub/v/shared_preferences.svg)](https://pub.dev/packages/shared_preferences) | [![pub points](https://img.shields.io/pub/points/shared_preferences)](https://pub.dev/packages/shared_preferences/score) | [![popularity](https://img.shields.io/pub/popularity/shared_preferences)](https://pub.dev/packages/shared_preferences/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20shared_preferences?label=)](https://github.com/flutter/flutter/labels/p%3A%20shared_preferences) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20shared_preferences?label=)](https://github.com/flutter/packages/labels/p%3A%20shared_preferences) | +| [standard\_message\_codec](./packages/standard_message_codec/) | [![pub package](https://img.shields.io/pub/v/standard_message_codec.svg)](https://pub.dev/packages/standard_message_codec) | [![pub points](https://img.shields.io/pub/points/standard_message_codec)](https://pub.dev/packages/standard_message_codec/score) | [![popularity](https://img.shields.io/pub/popularity/standard_message_codec)](https://pub.dev/packages/standard_message_codec/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20standard_message_codec?label=)](https://github.com/flutter/flutter/labels/p%3A%20standard_message_codec) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20standard_message_codec?label=)](https://github.com/flutter/packages/labels/p%3A%20standard_message_codec) | +| [two\_dimensional\_scrollables](./packages/two_dimensional_scrollables/) | [![pub package](https://img.shields.io/pub/v/two_dimensional_scrollables.svg)](https://pub.dev/packages/two_dimensional_scrollables) | [![pub points](https://img.shields.io/pub/points/two_dimensional_scrollables)](https://pub.dev/packages/two_dimensional_scrollables/score) | [![popularity](https://img.shields.io/pub/popularity/two_dimensional_scrollables)](https://pub.dev/packages/two_dimensional_scrollables/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20two_dimensional_scrollables?label=)](https://github.com/flutter/flutter/labels/p%3A%20two_dimensional_scrollables) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20two_dimensional_scrollables?label=)](https://github.com/flutter/packages/labels/p%3A%20two_dimensional_scrollables) | +| [url\_launcher](./packages/url_launcher/) | [![pub package](https://img.shields.io/pub/v/url_launcher.svg)](https://pub.dev/packages/url_launcher) | [![pub points](https://img.shields.io/pub/points/url_launcher)](https://pub.dev/packages/url_launcher/score) | [![popularity](https://img.shields.io/pub/popularity/url_launcher)](https://pub.dev/packages/url_launcher/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20url_launcher?label=)](https://github.com/flutter/flutter/labels/p%3A%20url_launcher) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20url_launcher?label=)](https://github.com/flutter/packages/labels/p%3A%20url_launcher) | +| [video\_player](./packages/video_player/) | [![pub package](https://img.shields.io/pub/v/video_player.svg)](https://pub.dev/packages/video_player) | [![pub points](https://img.shields.io/pub/points/video_player)](https://pub.dev/packages/video_player/score) | [![popularity](https://img.shields.io/pub/popularity/video_player)](https://pub.dev/packages/video_player/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20video_player?label=)](https://github.com/flutter/flutter/labels/p%3A%20video_player) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20video_player?label=)](https://github.com/flutter/packages/labels/p%3A%20video_player) | +| [web\_benchmarks](./packages/web_benchmarks/) | [![pub package](https://img.shields.io/pub/v/web_benchmarks.svg)](https://pub.dev/packages/web_benchmarks) | [![pub points](https://img.shields.io/pub/points/web_benchmarks)](https://pub.dev/packages/web_benchmarks/score) | [![popularity](https://img.shields.io/pub/popularity/web_benchmarks)](https://pub.dev/packages/web_benchmarks/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20web_benchmarks?label=)](https://github.com/flutter/flutter/labels/p%3A%20web_benchmarks) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20web_benchmarks?label=)](https://github.com/flutter/packages/labels/p%3A%20web_benchmarks) | +| [webview\_flutter](./packages/webview_flutter/) | [![pub package](https://img.shields.io/pub/v/webview_flutter.svg)](https://pub.dev/packages/webview_flutter) | [![pub points](https://img.shields.io/pub/points/webview_flutter)](https://pub.dev/packages/webview_flutter/score) | [![popularity](https://img.shields.io/pub/popularity/webview_flutter)](https://pub.dev/packages/webview_flutter/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20webview?label=)](https://github.com/flutter/flutter/labels/p%3A%20webview) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20webview_flutter?label=)](https://github.com/flutter/packages/labels/p%3A%20webview_flutter) | +| [xdg\_directories](./packages/xdg_directories/) | [![pub package](https://img.shields.io/pub/v/xdg_directories.svg)](https://pub.dev/packages/xdg_directories) | [![pub points](https://img.shields.io/pub/points/xdg_directories)](https://pub.dev/packages/xdg_directories/score) | [![popularity](https://img.shields.io/pub/popularity/xdg_directories)](https://pub.dev/packages/xdg_directories/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20xdg_directories?label=)](https://github.com/flutter/flutter/labels/p%3A%20xdg_directories) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20xdg_directories?label=)](https://github.com/flutter/packages/labels/p%3A%20xdg_directories) | From 25af3752c8a2f52e7a6bf13b66450821d1c02905 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Fri, 5 Jan 2024 14:28:44 -0500 Subject: [PATCH 07/15] Fix old pub.dartlang.org references --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 40321e57d624..0ca52af0003d 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ https://github.com/flutter/flutter). It contains the source code for Flutter's first-party packages (i.e., packages developed by the core Flutter team). Check the [`packages`](./packages) directory to see all packages. -These packages are also available on [pub](https://pub.dartlang.org/flutter/packages). +These packages are also available on [pub](https://pub.dev/flutter/packages). ## Issues @@ -25,7 +25,7 @@ see the documentation for [developing packages](https://flutter.io/developing-pa your package source code in any GitHub repository (the present repo is only intended for packages developed by the core Flutter team). Once your package is ready you can [publish](https://flutter.io/developing-packages/#publish) -to the [pub repository](https://pub.dartlang.org/). +to the [pub repository](https://pub.dev/). If you wish to contribute a change to any of the existing packages in this repo, please review our [contribution guide](https://github.com/flutter/packages/blob/main/CONTRIBUTING.md), @@ -61,7 +61,7 @@ These are the packages hosted in this repository: | [local\_auth](./packages/local_auth/) | [![pub package](https://img.shields.io/pub/v/local_auth.svg)](https://pub.dev/packages/local_auth) | [![pub points](https://img.shields.io/pub/points/local_auth)](https://pub.dev/packages/local_auth/score) | [![popularity](https://img.shields.io/pub/popularity/local_auth)](https://pub.dev/packages/local_auth/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20local_auth?label=)](https://github.com/flutter/flutter/labels/p%3A%20local_auth) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20local_auth?label=)](https://github.com/flutter/packages/labels/p%3A%20local_auth) | | [metrics\_center](./packages/metrics_center/) | [![pub package](https://img.shields.io/pub/v/metrics_center.svg)](https://pub.dev/packages/metrics_center) | [![pub points](https://img.shields.io/pub/points/metrics_center)](https://pub.dev/packages/metrics_center/score) | [![popularity](https://img.shields.io/pub/popularity/metrics_center)](https://pub.dev/packages/metrics_center/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20metrics_center?label=)](https://github.com/flutter/flutter/labels/p%3A%20metrics_center) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20metrics_center?label=)](https://github.com/flutter/packages/labels/p%3A%20metrics_center) | | [multicast\_dns](./packages/multicast_dns/) | [![pub package](https://img.shields.io/pub/v/multicast_dns.svg)](https://pub.dev/packages/multicast_dns) | [![pub points](https://img.shields.io/pub/points/multicast_dns)](https://pub.dev/packages/multicast_dns/score) | [![popularity](https://img.shields.io/pub/popularity/multicast_dns)](https://pub.dev/packages/multicast_dns/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20multicast_dns?label=)](https://github.com/flutter/flutter/labels/p%3A%20multicast_dns) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20multicast_dns?label=)](https://github.com/flutter/packages/labels/p%3A%20multicast_dns) | -| [palette\_generator](./packages/palette_generator/) | [![pub package](https://img.shields.io/pub/v/palette_generator.svg)](https://pub.dartlang.org/packages/palette_generator) | [![pub points](https://img.shields.io/pub/points/palette_generator)](https://pub.dev/packages/palette_generator/score) | [![popularity](https://img.shields.io/pub/popularity/palette_generator)](https://pub.dev/packages/palette_generator/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20palette_generator?label=)](https://github.com/flutter/flutter/labels/p%3A%20palette_generator) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20palette_generator?label=)](https://github.com/flutter/packages/labels/p%3A%20palette_generator) | +| [palette\_generator](./packages/palette_generator/) | [![pub package](https://img.shields.io/pub/v/palette_generator.svg)](https://pub.dev/packages/palette_generator) | [![pub points](https://img.shields.io/pub/points/palette_generator)](https://pub.dev/packages/palette_generator/score) | [![popularity](https://img.shields.io/pub/popularity/palette_generator)](https://pub.dev/packages/palette_generator/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20palette_generator?label=)](https://github.com/flutter/flutter/labels/p%3A%20palette_generator) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20palette_generator?label=)](https://github.com/flutter/packages/labels/p%3A%20palette_generator) | | [path\_provider](./packages/path_provider/) | [![pub package](https://img.shields.io/pub/v/path_provider.svg)](https://pub.dev/packages/path_provider) | [![pub points](https://img.shields.io/pub/points/path_provider)](https://pub.dev/packages/path_provider/score) | [![popularity](https://img.shields.io/pub/popularity/path_provider)](https://pub.dev/packages/path_provider/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20path_provider?label=)](https://github.com/flutter/flutter/labels/p%3A%20path_provider) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20path_provider?label=)](https://github.com/flutter/packages/labels/p%3A%20path_provider) | | [pigeon](./packages/pigeon/) | [![pub package](https://img.shields.io/pub/v/pigeon.svg)](https://pub.dev/packages/pigeon) | [![pub points](https://img.shields.io/pub/points/pigeon)](https://pub.dev/packages/pigeon/score) | [![popularity](https://img.shields.io/pub/popularity/pigeon)](https://pub.dev/packages/pigeon/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/pigeon?label=)](https://github.com/flutter/flutter/labels/pigeon) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20pigeon?label=)](https://github.com/flutter/packages/labels/p%3A%20pigeon) | | [platform](./packages/platform/) | [![pub package](https://img.shields.io/pub/v/platform.svg)](https://pub.dev/packages/platform) | [![pub points](https://img.shields.io/pub/points/platform)](https://pub.dev/packages/platform/score) | [![popularity](https://img.shields.io/pub/popularity/platform)](https://pub.dev/packages/platform/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20platform?label=)](https://github.com/flutter/flutter/labels/p%3A%20platform) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20platform?label=)](https://github.com/flutter/packages/labels/p%3A%20platform) | From 1e797bcfa64a635ecba5450c29a96926b6d7a42a Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Fri, 5 Jan 2024 14:30:32 -0500 Subject: [PATCH 08/15] Fix old pigeon tag usage --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0ca52af0003d..e4fa97fe1872 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ These are the packages hosted in this repository: | [multicast\_dns](./packages/multicast_dns/) | [![pub package](https://img.shields.io/pub/v/multicast_dns.svg)](https://pub.dev/packages/multicast_dns) | [![pub points](https://img.shields.io/pub/points/multicast_dns)](https://pub.dev/packages/multicast_dns/score) | [![popularity](https://img.shields.io/pub/popularity/multicast_dns)](https://pub.dev/packages/multicast_dns/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20multicast_dns?label=)](https://github.com/flutter/flutter/labels/p%3A%20multicast_dns) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20multicast_dns?label=)](https://github.com/flutter/packages/labels/p%3A%20multicast_dns) | | [palette\_generator](./packages/palette_generator/) | [![pub package](https://img.shields.io/pub/v/palette_generator.svg)](https://pub.dev/packages/palette_generator) | [![pub points](https://img.shields.io/pub/points/palette_generator)](https://pub.dev/packages/palette_generator/score) | [![popularity](https://img.shields.io/pub/popularity/palette_generator)](https://pub.dev/packages/palette_generator/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20palette_generator?label=)](https://github.com/flutter/flutter/labels/p%3A%20palette_generator) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20palette_generator?label=)](https://github.com/flutter/packages/labels/p%3A%20palette_generator) | | [path\_provider](./packages/path_provider/) | [![pub package](https://img.shields.io/pub/v/path_provider.svg)](https://pub.dev/packages/path_provider) | [![pub points](https://img.shields.io/pub/points/path_provider)](https://pub.dev/packages/path_provider/score) | [![popularity](https://img.shields.io/pub/popularity/path_provider)](https://pub.dev/packages/path_provider/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20path_provider?label=)](https://github.com/flutter/flutter/labels/p%3A%20path_provider) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20path_provider?label=)](https://github.com/flutter/packages/labels/p%3A%20path_provider) | -| [pigeon](./packages/pigeon/) | [![pub package](https://img.shields.io/pub/v/pigeon.svg)](https://pub.dev/packages/pigeon) | [![pub points](https://img.shields.io/pub/points/pigeon)](https://pub.dev/packages/pigeon/score) | [![popularity](https://img.shields.io/pub/popularity/pigeon)](https://pub.dev/packages/pigeon/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/pigeon?label=)](https://github.com/flutter/flutter/labels/pigeon) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20pigeon?label=)](https://github.com/flutter/packages/labels/p%3A%20pigeon) | +| [pigeon](./packages/pigeon/) | [![pub package](https://img.shields.io/pub/v/pigeon.svg)](https://pub.dev/packages/pigeon) | [![pub points](https://img.shields.io/pub/points/pigeon)](https://pub.dev/packages/pigeon/score) | [![popularity](https://img.shields.io/pub/popularity/pigeon)](https://pub.dev/packages/pigeon/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20pigeon?label=)](https://github.com/flutter/flutter/labels/p%3A%20pigeon) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20pigeon?label=)](https://github.com/flutter/packages/labels/p%3A%20pigeon) | | [platform](./packages/platform/) | [![pub package](https://img.shields.io/pub/v/platform.svg)](https://pub.dev/packages/platform) | [![pub points](https://img.shields.io/pub/points/platform)](https://pub.dev/packages/platform/score) | [![popularity](https://img.shields.io/pub/popularity/platform)](https://pub.dev/packages/platform/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20platform?label=)](https://github.com/flutter/flutter/labels/p%3A%20platform) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20platform?label=)](https://github.com/flutter/packages/labels/p%3A%20platform) | | [pointer\_interceptor](./packages/pointer_interceptor/) | [![pub package](https://img.shields.io/pub/v/pointer_interceptor.svg)](https://pub.dev/packages/pointer_interceptor) | [![pub points](https://img.shields.io/pub/points/pointer_interceptor)](https://pub.dev/packages/pointer_interceptor/score) | [![popularity](https://img.shields.io/pub/popularity/pointer_interceptor)](https://pub.dev/packages/pointer_interceptor/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20pointer_interceptor?label=)](https://github.com/flutter/flutter/labels/p%3A%20pointer_interceptor) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20pointer_interceptor?label=)](https://github.com/flutter/packages/labels/p%3A%20pointer_interceptor) | | [plugin\_platform\_interface](./packages/plugin_platform_interface/) | [![pub package](https://img.shields.io/pub/v/plugin_platform_interface.svg)](https://pub.dev/packages/plugin_platform_interface) | [![pub points](https://img.shields.io/pub/points/plugin_platform_interface)](https://pub.dev/packages/plugin_platform_interface/score) | [![popularity](https://img.shields.io/pub/popularity/plugin_platform_interface)](https://pub.dev/packages/plugin_platform_interface/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20plugin_platform_interface?label=)](https://github.com/flutter/flutter/labels/p%3A%20plugin_platform_interface) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20plugin_platform_interface?label=)](https://github.com/flutter/packages/labels/p%3A%20plugin_platform_interface) | From 33955452ddcded3ee92e5ad22181d1e0be567efe Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Fri, 5 Jan 2024 14:38:56 -0500 Subject: [PATCH 09/15] Add special-cased tags --- .../src/repo_package_info_check_command.dart | 42 +++++++++++++++---- .../repo_package_info_check_command_test.dart | 4 +- 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/script/tool/lib/src/repo_package_info_check_command.dart b/script/tool/lib/src/repo_package_info_check_command.dart index ff83781b0100..b078617690a7 100644 --- a/script/tool/lib/src/repo_package_info_check_command.dart +++ b/script/tool/lib/src/repo_package_info_check_command.dart @@ -97,7 +97,10 @@ class RepoPackageInfoCheckCommand extends PackageLoopingCommand { '${indentation}Invalid repo root README.md table entry: "$cell"'); errors.add('Invalid root README.md table entry'); } else { - final String encodedTag = Uri.encodeComponent('p: $packageName'); + final String encodedIssueTag = + Uri.encodeComponent(_issueTagForPackage(packageName)); + final String encodedPRTag = + Uri.encodeComponent(_prTagForPackage(packageName)); final String anchor = match.group(1)!; final String target = match.group(2)!; @@ -109,14 +112,19 @@ class RepoPackageInfoCheckCommand extends PackageLoopingCommand { RegExp(r'^!\[.*\]\(https://img.shields.io/pub/.*/' '$packageName' r'(?:\.svg)?\)$'); - final RegExp tagLink = - RegExp(r'^!\[.*\]\(https://img.shields.io/github/.*/' - '$encodedTag' - r'\?label=\)$'); + final RegExp issueTagLink = RegExp( + r'^!\[.*\]\(https://img.shields.io/github/issues/flutter/flutter/' + '$encodedIssueTag' + r'\?label=\)$'); + final RegExp prTagLink = RegExp( + r'^!\[.*\]\(https://img.shields.io/github/issues-pr/flutter/packages/' + '$encodedPRTag' + r'\?label=\)$'); if (!(anchor == packageName || anchor == packageName.replaceAll('_', r'\_') || packageLink.hasMatch(anchor) || - tagLink.hasMatch(anchor))) { + issueTagLink.hasMatch(anchor) || + prTagLink.hasMatch(anchor))) { printError( '${indentation}Incorrect anchor in root README.md table: "$anchor"'); errors.add('Incorrect anchor in root README.md table'); @@ -128,11 +136,14 @@ class RepoPackageInfoCheckCommand extends PackageLoopingCommand { // - a github label link to the package's label final RegExp pubDevLink = RegExp('^https://pub.dev/packages/$packageName(?:/score)?\$'); - final RegExp gitHubLink = RegExp( - '^https://github.com/flutter/(?:flutter|packages)/labels/$encodedTag\$'); + final RegExp gitHubIssueLink = RegExp( + '^https://github.com/flutter/flutter/labels/$encodedIssueTag\$'); + final RegExp gitHubPRLink = RegExp( + '^https://github.com/flutter/packages/labels/$encodedPRTag\$'); if (!(target == './packages/$packageName/' || pubDevLink.hasMatch(target) || - gitHubLink.hasMatch(target))) { + gitHubIssueLink.hasMatch(target) || + gitHubPRLink.hasMatch(target))) { printError( '${indentation}Incorrect link in root README.md table: "$target"'); errors.add('Incorrect link in root README.md table'); @@ -146,4 +157,17 @@ class RepoPackageInfoCheckCommand extends PackageLoopingCommand { ? PackageResult.success() : PackageResult.fail(errors); } + + String _prTagForPackage(String packageName) => 'p: $packageName'; + + String _issueTagForPackage(String packageName) { + switch (packageName) { + case 'google_maps_flutter': + return 'p: maps'; + case 'webview_flutter': + return 'p: webview'; + default: + return 'p: $packageName'; + } + } } diff --git a/script/tool/test/repo_package_info_check_command_test.dart b/script/tool/test/repo_package_info_check_command_test.dart index 16d165d17b34..bee997ef1d57 100644 --- a/script/tool/test/repo_package_info_check_command_test.dart +++ b/script/tool/test/repo_package_info_check_command_test.dart @@ -43,8 +43,8 @@ void main() { '''; } - String readmeTableEntry(String packageName, {String? tag}) { - final String encodedTag = Uri.encodeComponent(tag ?? 'p: $packageName'); + String readmeTableEntry(String packageName) { + final String encodedTag = Uri.encodeComponent('p: $packageName'); return '| [$packageName](./packages/$packageName/) | ' '[![pub package](https://img.shields.io/pub/v/$packageName.svg)](https://pub.dev/packages/$packageName) | ' '[![pub points](https://img.shields.io/pub/points/$packageName)](https://pub.dev/packages/$packageName/score) | ' From 770af8f94b9b820239d39be20c07f47e1788e1b6 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Fri, 5 Jan 2024 14:40:02 -0500 Subject: [PATCH 10/15] Minor edge case fixes --- README.md | 2 +- script/tool/lib/src/repo_package_info_check_command.dart | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e4fa97fe1872..ac0078ff5f72 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ These are the packages hosted in this repository: | [flutter\_template\_images](./packages/flutter_template_images/) | [![pub package](https://img.shields.io/pub/v/flutter_template_images.svg)](https://pub.dev/packages/flutter_template_images) | [![pub points](https://img.shields.io/pub/points/flutter_template_images)](https://pub.dev/packages/flutter_template_images/score) | [![popularity](https://img.shields.io/pub/popularity/flutter_template_images)](https://pub.dev/packages/flutter_template_images/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20flutter_template_images?label=)](https://github.com/flutter/flutter/labels/p%3A%20flutter_template_images) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20flutter_template_images?label=)](https://github.com/flutter/packages/labels/p%3A%20flutter_template_images) | | [go\_router](./packages/go_router/) | [![pub package](https://img.shields.io/pub/v/go_router.svg)](https://pub.dev/packages/go_router) | [![pub points](https://img.shields.io/pub/points/go_router)](https://pub.dev/packages/go_router/score) | [![popularity](https://img.shields.io/pub/popularity/go_router)](https://pub.dev/packages/go_router/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20go_router?label=)](https://github.com/flutter/flutter/labels/p%3A%20go_router) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20go_router?label=)](https://github.com/flutter/packages/labels/p%3A%20go_router) | | [go\_router\_builder](./packages/go_router_builder/) | [![pub package](https://img.shields.io/pub/v/go_router_builder.svg)](https://pub.dev/packages/go_router_builder) | [![pub points](https://img.shields.io/pub/points/go_router_builder)](https://pub.dev/packages/go_router_builder/score) | [![popularity](https://img.shields.io/pub/popularity/go_router_builder)](https://pub.dev/packages/go_router_builder/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20go_router_builder?label=)](https://github.com/flutter/flutter/labels/p%3A%20go_router_builder) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20go_router_builder?label=)](https://github.com/flutter/packages/labels/p%3A%20go_router_builder) | -| [google\_maps\_flutter](./packages/google_maps_flutter) | [![pub package](https://img.shields.io/pub/v/google_maps_flutter.svg)](https://pub.dev/packages/google_maps_flutter) | [![pub points](https://img.shields.io/pub/points/google_maps_flutter)](https://pub.dev/packages/google_maps_flutter/score) | [![popularity](https://img.shields.io/pub/popularity/google_maps_flutter)](https://pub.dev/packages/google_maps_flutter/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20maps?label=)](https://github.com/flutter/flutter/labels/p%3A%20maps) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20google_maps_flutter?label=)](https://github.com/flutter/packages/labels/p%3A%20google_maps_flutter) | +| [google\_maps\_flutter](./packages/google_maps_flutter/) | [![pub package](https://img.shields.io/pub/v/google_maps_flutter.svg)](https://pub.dev/packages/google_maps_flutter) | [![pub points](https://img.shields.io/pub/points/google_maps_flutter)](https://pub.dev/packages/google_maps_flutter/score) | [![popularity](https://img.shields.io/pub/popularity/google_maps_flutter)](https://pub.dev/packages/google_maps_flutter/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20maps?label=)](https://github.com/flutter/flutter/labels/p%3A%20maps) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20google_maps_flutter?label=)](https://github.com/flutter/packages/labels/p%3A%20google_maps_flutter) | | [google\_sign\_in](./packages/google_sign_in/) | [![pub package](https://img.shields.io/pub/v/google_sign_in.svg)](https://pub.dev/packages/google_sign_in) | [![pub points](https://img.shields.io/pub/points/google_sign_in)](https://pub.dev/packages/google_sign_in/score) | [![popularity](https://img.shields.io/pub/popularity/google_sign_in)](https://pub.dev/packages/google_sign_in/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20google_sign_in?label=)](https://github.com/flutter/flutter/labels/p%3A%20google_sign_in) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20google_sign_in?label=)](https://github.com/flutter/packages/labels/p%3A%20google_sign_in) | | [image\_picker](./packages/image_picker/) | [![pub package](https://img.shields.io/pub/v/image_picker.svg)](https://pub.dev/packages/image_picker) | [![pub points](https://img.shields.io/pub/points/image_picker)](https://pub.dev/packages/image_picker/score) | [![popularity](https://img.shields.io/pub/popularity/image_picker)](https://pub.dev/packages/image_picker/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20image_picker?label=)](https://github.com/flutter/flutter/labels/p%3A%20image_picker) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20image_picker?label=)](https://github.com/flutter/packages/labels/p%3A%20image_picker) | | [in\_app\_purchase](./packages/in_app_purchase/) | [![pub package](https://img.shields.io/pub/v/in_app_purchase.svg)](https://pub.dev/packages/in_app_purchase) | [![pub points](https://img.shields.io/pub/points/in_app_purchase)](https://pub.dev/packages/in_app_purchase/score) | [![popularity](https://img.shields.io/pub/popularity/in_app_purchase)](https://pub.dev/packages/in_app_purchase/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20in_app_purchase?label=)](https://github.com/flutter/flutter/labels/p%3A%20in_app_purchase) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20in_app_purchase?label=)](https://github.com/flutter/packages/labels/p%3A%20in_app_purchase) | diff --git a/script/tool/lib/src/repo_package_info_check_command.dart b/script/tool/lib/src/repo_package_info_check_command.dart index b078617690a7..be9471895076 100644 --- a/script/tool/lib/src/repo_package_info_check_command.dart +++ b/script/tool/lib/src/repo_package_info_check_command.dart @@ -141,6 +141,7 @@ class RepoPackageInfoCheckCommand extends PackageLoopingCommand { final RegExp gitHubPRLink = RegExp( '^https://github.com/flutter/packages/labels/$encodedPRTag\$'); if (!(target == './packages/$packageName/' || + target == './third_party/packages/$packageName/' || pubDevLink.hasMatch(target) || gitHubIssueLink.hasMatch(target) || gitHubPRLink.hasMatch(target))) { From 8474a7efd499e4e15c640577c7376264bfb96fa2 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Fri, 5 Jan 2024 16:33:14 -0500 Subject: [PATCH 11/15] Validate CODEOWNERS --- .../src/repo_package_info_check_command.dart | 35 ++++++- .../repo_package_info_check_command_test.dart | 98 ++++++++++++++++--- 2 files changed, 117 insertions(+), 16 deletions(-) diff --git a/script/tool/lib/src/repo_package_info_check_command.dart b/script/tool/lib/src/repo_package_info_check_command.dart index be9471895076..2e67d1ac6d52 100644 --- a/script/tool/lib/src/repo_package_info_check_command.dart +++ b/script/tool/lib/src/repo_package_info_check_command.dart @@ -20,9 +20,13 @@ class RepoPackageInfoCheckCommand extends PackageLoopingCommand { late Directory _repoRoot; + /// Data from the root README.md table of packages. final Map> _readmeTableEntries = >{}; + /// Packages with entries in CODEOWNERS. + final List _ownedPackages = []; + @override final String name = 'repo-package-info-check'; @@ -40,6 +44,7 @@ class RepoPackageInfoCheckCommand extends PackageLoopingCommand { Future initializeRun() async { _repoRoot = packagesDir.fileSystem.directory((await gitDir).path); + // Extract all of the README.md table entries. final RegExp namePattern = RegExp(r'\[(.*?)\]\('); for (final String line in _repoRoot.childFile('README.md').readAsLinesSync()) { @@ -68,19 +73,45 @@ class RepoPackageInfoCheckCommand extends PackageLoopingCommand { } } } + + // Extract all of the CODEOWNERS package entries. + final RegExp packageOwnershipPattern = + RegExp(r'^((?:third_party/)?packages/(?:[^/]*/)?([^/]*))/\*\*'); + for (final String line + in _repoRoot.childFile('CODEOWNERS').readAsLinesSync()) { + final RegExpMatch? match = packageOwnershipPattern.firstMatch(line); + if (match == null) { + continue; + } + final String path = match.group(1)!; + final String name = match.group(2)!; + if (!_repoRoot.childDirectory(path).existsSync()) { + printError('Unknown directory "$path" in CODEOWNERS'); + throw ToolExit(_exitUknownPackageEntry); + } + _ownedPackages.add(name); + } } @override Future runForPackage(RepositoryPackage package) async { + final String packageName = package.directory.basename; final List errors = []; - // All packages should have + // All packages should have an owner. + // Platform interface packages are considered to be owned by the app-facing + // package owner. + if (!(_ownedPackages.contains(packageName) || + package.isPlatformInterface && + _ownedPackages.contains(package.directory.parent.basename))) { + printError('${indentation}Missing CODEOWNERS entry.'); + errors.add('Missing CODEOWNERS entry'); + } // Any published package should be in the README table. // For federated plugins, only the app-facing package is listed. if (package.isPublishable() && (!package.isFederated || package.isAppFacing)) { - final String packageName = package.directory.basename; final List? cells = _readmeTableEntries[packageName]; if (cells == null) { diff --git a/script/tool/test/repo_package_info_check_command_test.dart b/script/tool/test/repo_package_info_check_command_test.dart index bee997ef1d57..7aa5e1621e57 100644 --- a/script/tool/test/repo_package_info_check_command_test.dart +++ b/script/tool/test/repo_package_info_check_command_test.dart @@ -43,6 +43,18 @@ void main() { '''; } + void writeCodeOwners(List ownedPackages) { + final List subpaths = ownedPackages + .map((RepositoryPackage p) => p.isFederated + ? [p.directory.parent.basename, p.directory.basename] + .join('/') + : p.directory.basename) + .toList(); + root.childFile('CODEOWNERS').writeAsStringSync(''' +${subpaths.map((String subpath) => 'packages/$subpath/** @someone').join('\n')} +'''); + } + String readmeTableEntry(String packageName) { final String encodedTag = Uri.encodeComponent('p: $packageName'); return '| [$packageName](./packages/$packageName/) | ' @@ -54,12 +66,15 @@ void main() { } test('passes for correct README coverage', () async { - createFakePackage('a_package', packagesDir); + final List packages = [ + createFakePackage('a_package', packagesDir), + ]; root.childFile('README.md').writeAsStringSync(''' ${readmeTableHeader()} ${readmeTableEntry('a_package')} '''); + writeCodeOwners(packages); final List output = await runCapturingPrint(runner, ['repo-package-info-check']); @@ -72,15 +87,18 @@ ${readmeTableEntry('a_package')} () async { const String pluginName = 'foo'; final Directory pluginDir = packagesDir.childDirectory(pluginName); - createFakePlugin(pluginName, pluginDir); - createFakePlugin('${pluginName}_platform_interface', pluginDir); - createFakePlugin('${pluginName}_android', pluginDir); - createFakePlugin('${pluginName}_ios', pluginDir); + final List packages = [ + createFakePlugin(pluginName, pluginDir), + createFakePlugin('${pluginName}_platform_interface', pluginDir), + createFakePlugin('${pluginName}_android', pluginDir), + createFakePlugin('${pluginName}_ios', pluginDir), + ]; root.childFile('README.md').writeAsStringSync(''' ${readmeTableHeader()} ${readmeTableEntry(pluginName)} '''); + writeCodeOwners(packages); final List output = await runCapturingPrint(runner, ['repo-package-info-check']); @@ -90,12 +108,15 @@ ${readmeTableEntry(pluginName)} }); test('fails for unexpected README table entry', () async { - createFakePackage('a_package', packagesDir); + final List packages = [ + createFakePackage('a_package', packagesDir), + ]; root.childFile('README.md').writeAsStringSync(''' ${readmeTableHeader()} ${readmeTableEntry('another_package')} '''); + writeCodeOwners(packages); Error? commandError; final List output = await runCapturingPrint( @@ -112,13 +133,16 @@ ${readmeTableEntry('another_package')} }); test('fails for missing README table entry', () async { - createFakePackage('a_package', packagesDir); - createFakePackage('another_package', packagesDir); + final List packages = [ + createFakePackage('a_package', packagesDir), + createFakePackage('another_package', packagesDir), + ]; root.childFile('README.md').writeAsStringSync(''' ${readmeTableHeader()} ${readmeTableEntry('another_package')} '''); + writeCodeOwners(packages); Error? commandError; final List output = await runCapturingPrint( @@ -139,7 +163,9 @@ ${readmeTableEntry('another_package')} test('fails for unexpected format in README table entry', () async { const String packageName = 'a_package'; final String encodedTag = Uri.encodeComponent('p: $packageName'); - createFakePackage('a_package', packagesDir); + final List packages = [ + createFakePackage('a_package', packagesDir), + ]; final String entry = '| [$packageName](./packages/$packageName/) | ' 'Some random text | ' @@ -152,6 +178,7 @@ ${readmeTableEntry('another_package')} ${readmeTableHeader()} $entry '''); + writeCodeOwners(packages); Error? commandError; final List output = await runCapturingPrint( @@ -174,7 +201,9 @@ $entry const String packageName = 'a_package'; final String encodedTag = Uri.encodeComponent('p: $packageName'); const String incorrectPackageName = 'a_pakage'; - createFakePackage('a_package', packagesDir); + final List packages = [ + createFakePackage('a_package', packagesDir), + ]; final String entry = '| [$packageName](./packages/$incorrectPackageName/) | ' @@ -188,6 +217,7 @@ $entry ${readmeTableHeader()} $entry '''); + writeCodeOwners(packages); Error? commandError; final List output = await runCapturingPrint( @@ -210,7 +240,9 @@ $entry const String packageName = 'a_package'; final String encodedTag = Uri.encodeComponent('p: $packageName'); const String incorrectPackageName = 'a_pakage'; - createFakePackage('a_package', packagesDir); + final List packages = [ + createFakePackage('a_package', packagesDir), + ]; final String entry = '| [$packageName](./packages/$packageName/) | ' '[![pub package](https://img.shields.io/pub/v/$packageName.svg)](https://pub.dev/packages/$packageName) | ' @@ -223,6 +255,7 @@ $entry ${readmeTableHeader()} $entry '''); + writeCodeOwners(packages); Error? commandError; final List output = await runCapturingPrint( @@ -245,7 +278,9 @@ $entry const String packageName = 'a_package'; final String encodedTag = Uri.encodeComponent('p: $packageName'); final String incorrectTag = Uri.encodeComponent('p: a_pakage'); - createFakePackage('a_package', packagesDir); + final List packages = [ + createFakePackage('a_package', packagesDir), + ]; final String entry = '| [$packageName](./packages/$packageName/) | ' '[![pub package](https://img.shields.io/pub/v/$packageName.svg)](https://pub.dev/packages/$packageName) | ' @@ -258,6 +293,7 @@ $entry ${readmeTableHeader()} $entry '''); + writeCodeOwners(packages); Error? commandError; final List output = await runCapturingPrint( @@ -280,7 +316,9 @@ $entry const String packageName = 'a_package'; final String encodedTag = Uri.encodeComponent('p: $packageName'); const String incorrectPackageName = 'a_pakage'; - createFakePackage('a_package', packagesDir); + final List packages = [ + createFakePackage('a_package', packagesDir), + ]; final String entry = '| [$packageName](./packages/$packageName/) | ' '[![pub package](https://img.shields.io/pub/v/$packageName.svg)](https://pub.dev/packages/$packageName) | ' @@ -293,6 +331,7 @@ $entry ${readmeTableHeader()} $entry '''); + writeCodeOwners(packages); Error? commandError; final List output = await runCapturingPrint( @@ -315,7 +354,9 @@ $entry const String packageName = 'a_package'; final String encodedTag = Uri.encodeComponent('p: $packageName'); final String incorrectTag = Uri.encodeComponent('p: a_pakage'); - createFakePackage('a_package', packagesDir); + final List packages = [ + createFakePackage('a_package', packagesDir), + ]; final String entry = '| [$packageName](./packages/$packageName/) | ' '[![pub package](https://img.shields.io/pub/v/$packageName.svg)](https://pub.dev/packages/$packageName) | ' @@ -328,6 +369,7 @@ $entry ${readmeTableHeader()} $entry '''); + writeCodeOwners(packages); Error? commandError; final List output = await runCapturingPrint( @@ -345,4 +387,32 @@ $entry ' Incorrect anchor in root README.md table') ])); }); + + test('fails for missing CODEOWNER', () async { + const String packageName = 'a_package'; + final String encodedTag = Uri.encodeComponent('p: $packageName'); + final String incorrectTag = Uri.encodeComponent('p: a_pakage'); + createFakePackage('a_package', packagesDir); + + root.childFile('README.md').writeAsStringSync(''' +${readmeTableHeader()} +${readmeTableEntry('a_package')} +'''); + writeCodeOwners([]); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['repo-package-info-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('Missing CODEOWNERS entry.'), + contains('a_package:\n' + ' Missing CODEOWNERS entry') + ])); + }); } From 7dcbd579777cac31ecb6fa1e5de03b562833a291 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Fri, 5 Jan 2024 16:33:27 -0500 Subject: [PATCH 12/15] Make CODEOWNERS compliant --- CODEOWNERS | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 16c60820938c..fab0a1e6b7ae 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -50,7 +50,16 @@ third_party/packages/cupertino_icons/** @MitchellGoodwin # matching entry takes precedence. # - Web -packages/**/*_web/** @ditman +packages/camera/camera_web/** @ditman +packages/file_selector/file_selector_web/** @ditman +packages/google_maps_flutter/google_maps_flutter_web/** @ditman +packages/google_sign_in/google_sign_in_web/** @ditman +packages/image_picker/image_picker_for_web/** @ditman +packages/pointer_interceptor/pointer_interceptor_web/** @ditman +packages/shared_preferences/shared_preferences_web/** @ditman +packages/url_launcher/url_launcher_web/** @ditman +packages/video_player/video_player_web/** @ditman +packages/webview_flutter/webview_flutter_web/** @ditman # - Android packages/camera/camera_android/** @camsim99 @@ -68,6 +77,8 @@ packages/quick_actions/quick_actions_android/** @camsim99 packages/shared_preferences/shared_preferences_android/** @reidbaker packages/url_launcher/url_launcher_android/** @gmackall packages/video_player/video_player_android/** @camsim99 +# Owned by ecosystem team for now during the wrapper evaluation. +packages/webview_flutter/webview_flutter_android/** @bparrishMines # - iOS packages/camera/camera_avfoundation/** @hellohuanlin @@ -76,9 +87,10 @@ packages/google_maps_flutter/google_maps_flutter_ios/** @hellohuanlin packages/google_sign_in/google_sign_in_ios/** @vashworth packages/image_picker/image_picker_ios/** @vashworth packages/in_app_purchase/in_app_purchase_storekit/** @louisehsu -packages/ios_platform_images/ios/** @jmagman +packages/ios_platform_images/** @jmagman packages/local_auth/local_auth_ios/** @louisehsu packages/path_provider/path_provider_foundation/** @jmagman +packages/pointer_interceptor/pointer_interceptor_ios/** @ditman packages/quick_actions/quick_actions_ios/** @hellohuanlin packages/shared_preferences/shared_preferences_foundation/** @tarrinneal packages/url_launcher/url_launcher_ios/** @jmagman From c1c61281b0c136956a6532094596015f11fb3882 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Fri, 5 Jan 2024 16:35:07 -0500 Subject: [PATCH 13/15] Add to CI --- .ci/targets/repo_checks.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.ci/targets/repo_checks.yaml b/.ci/targets/repo_checks.yaml index 3f03560594fc..5989a5efed78 100644 --- a/.ci/targets/repo_checks.yaml +++ b/.ci/targets/repo_checks.yaml @@ -40,6 +40,10 @@ tasks: script: script/tool_runner.sh args: ["gradle-check"] always: true + - name: Repository-level package metadata validation + script: script/tool_runner.sh + args: ["check-repo-package-info"] + always: true - name: Dependabot coverage validation script: script/tool_runner.sh args: ["dependabot-check"] From ad60655c60f1bf9c95dacf2e9f15934b268c5997 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Fri, 5 Jan 2024 16:35:47 -0500 Subject: [PATCH 14/15] Analysis --- script/tool/test/repo_package_info_check_command_test.dart | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/script/tool/test/repo_package_info_check_command_test.dart b/script/tool/test/repo_package_info_check_command_test.dart index 7aa5e1621e57..68f88cbccd89 100644 --- a/script/tool/test/repo_package_info_check_command_test.dart +++ b/script/tool/test/repo_package_info_check_command_test.dart @@ -390,13 +390,11 @@ $entry test('fails for missing CODEOWNER', () async { const String packageName = 'a_package'; - final String encodedTag = Uri.encodeComponent('p: $packageName'); - final String incorrectTag = Uri.encodeComponent('p: a_pakage'); - createFakePackage('a_package', packagesDir); + createFakePackage(packageName, packagesDir); root.childFile('README.md').writeAsStringSync(''' ${readmeTableHeader()} -${readmeTableEntry('a_package')} +${readmeTableEntry(packageName)} '''); writeCodeOwners([]); From a28b80a8b5c0f8febcc444b2cab90a18bb3f0089 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 12 Jan 2024 06:40:46 -0500 Subject: [PATCH 15/15] Fix typo --- script/tool/lib/src/repo_package_info_check_command.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/script/tool/lib/src/repo_package_info_check_command.dart b/script/tool/lib/src/repo_package_info_check_command.dart index 2e67d1ac6d52..5369f6eede0e 100644 --- a/script/tool/lib/src/repo_package_info_check_command.dart +++ b/script/tool/lib/src/repo_package_info_check_command.dart @@ -10,7 +10,7 @@ import 'common/package_looping_command.dart'; import 'common/repository_package.dart'; const int _exitBadTableEntry = 3; -const int _exitUknownPackageEntry = 4; +const int _exitUnknownPackageEntry = 4; /// A command to verify repository-level metadata about packages, such as /// repo README and CODEOWNERS entries. @@ -69,7 +69,7 @@ class RepoPackageInfoCheckCommand extends PackageLoopingCommand { if (!(packagesDir.childDirectory(name).existsSync() || thirdPartyPackagesDir.childDirectory(name).existsSync())) { printError('Unknown package "$name" in root README.md table.'); - throw ToolExit(_exitUknownPackageEntry); + throw ToolExit(_exitUnknownPackageEntry); } } } @@ -87,7 +87,7 @@ class RepoPackageInfoCheckCommand extends PackageLoopingCommand { final String name = match.group(2)!; if (!_repoRoot.childDirectory(path).existsSync()) { printError('Unknown directory "$path" in CODEOWNERS'); - throw ToolExit(_exitUknownPackageEntry); + throw ToolExit(_exitUnknownPackageEntry); } _ownedPackages.add(name); }