From d74d687dc0aaf3918591b9e913322ae01ef8ef00 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 12 Jan 2024 06:11:30 -0800 Subject: [PATCH] [ci] Check repo-level package metadata (#5811) Adds a new tool command (and runs it in CI) to check that each package: - is listed correctly in the repo-level README.md table - has a CODEOWNERS entry In the future we could add other things (e.g., auto-label), but these were the main things we've had issues with recently. Updates README.md and CODEOWNERS to fix failures it found: - Adds a couple of missing CODEOWNERS - Expands the web implementation CODEOWNERS to individual packages so that we don't have to special-case handling in the tool - Fixes some minor mistakes in README.md - URL-encodes all `:`s in the README.md links (which is why ever line shows as changed); it worked without that in practice, but it should really be encoded, and having it consistently encoded made things easier for the tooling. --- .ci/targets/repo_checks.yaml | 4 + CODEOWNERS | 16 +- README.md | 86 ++-- .../lib/src/common/repository_package.dart | 5 + script/tool/lib/src/main.dart | 2 + .../src/repo_package_info_check_command.dart | 205 +++++++++ .../repo_package_info_check_command_test.dart | 416 ++++++++++++++++++ 7 files changed, 690 insertions(+), 44 deletions(-) create mode 100644 script/tool/lib/src/repo_package_info_check_command.dart create mode 100644 script/tool/test/repo_package_info_check_command_test.dart 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"] 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 diff --git a/README.md b/README.md index ce98d9b7d138..ac0078ff5f72 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), @@ -37,43 +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) | -| [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) | -| [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.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/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) | +| [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) | 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..5369f6eede0e --- /dev/null +++ b/script/tool/lib/src/repo_package_info_check_command.dart @@ -0,0 +1,205 @@ +// 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 _exitUnknownPackageEntry = 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; + + /// 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'; + + @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); + + // Extract all of the README.md table entries. + 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('Unknown package "$name" in root README.md table.'); + throw ToolExit(_exitUnknownPackageEntry); + } + } + } + + // 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(_exitUnknownPackageEntry); + } + _ownedPackages.add(name); + } + } + + @override + Future runForPackage(RepositoryPackage package) async { + final String packageName = package.directory.basename; + final List errors = []; + + // 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 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 encodedIssueTag = + Uri.encodeComponent(_issueTagForPackage(packageName)); + final String encodedPRTag = + Uri.encodeComponent(_prTagForPackage(packageName)); + final String anchor = match.group(1)!; + final String target = match.group(2)!; + + // 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 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) || + 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'); + } + + // 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 + final RegExp pubDevLink = + RegExp('^https://pub.dev/packages/$packageName(?:/score)?\$'); + 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/' || + target == './third_party/packages/$packageName/' || + pubDevLink.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'); + } + } + } + } + } + + return errors.isEmpty + ? 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 new file mode 100644 index 000000000000..68f88cbccd89 --- /dev/null +++ b/script/tool/test/repo_package_info_check_command_test.dart @@ -0,0 +1,416 @@ +// 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 | +|---------|-----|--------|------------|--------|---------------| +'''; + } + + 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/) | ' + '[![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 { + 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']); + + 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); + 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']); + + expect(output, + containsAllInOrder([contains('Ran for 4 package(s)')])); + }); + + test('fails for unexpected README table entry', () async { + 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( + 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 { + 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( + 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') + ])); + }); + + test('fails for unexpected format in README table entry', () async { + const String packageName = 'a_package'; + final String encodedTag = Uri.encodeComponent('p: $packageName'); + final List packages = [ + 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 +'''); + writeCodeOwners(packages); + + 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 source link in README table entry', () async { + const String packageName = 'a_package'; + final String encodedTag = Uri.encodeComponent('p: $packageName'); + const String incorrectPackageName = 'a_pakage'; + final List packages = [ + 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 +'''); + writeCodeOwners(packages); + + 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('p: $packageName'); + const String incorrectPackageName = 'a_pakage'; + 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) | ' + '[![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 +'''); + writeCodeOwners(packages); + + 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('p: $packageName'); + final String incorrectTag = Uri.encodeComponent('p: a_pakage'); + 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) | ' + '[![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 +'''); + writeCodeOwners(packages); + + 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') + ])); + }); + + 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'; + 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) | ' + '[![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 +'''); + writeCodeOwners(packages); + + 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'); + 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) | ' + '[![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 +'''); + writeCodeOwners(packages); + + 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') + ])); + }); + + test('fails for missing CODEOWNER', () async { + const String packageName = 'a_package'; + createFakePackage(packageName, packagesDir); + + root.childFile('README.md').writeAsStringSync(''' +${readmeTableHeader()} +${readmeTableEntry(packageName)} +'''); + 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') + ])); + }); +}