From 2b0eb9e78d26ac414a07f8da9f0c318740e2fa6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Ali=20Ustao=C4=9Flu?= Date: Sat, 24 Oct 2026 17:39:57 +0200 Subject: [PATCH] tool: the weekly check resolves an unpublished package from here only sqflite_watchos is staged at 0.0.1 and not on pub.dev, so the weekly upstream check failed on it: "pub.dev has no version of sqflite_watchos." (run 40106378500, the only red row). A package without a row in tool/published.yaml now resolves from this repository only, with a note. A package with a row that pub.dev does not know still fails. bin/upstream_compat.dart reads tool/published.yaml, or --published. --- tool/README.md | 4 +- tool/bin/upstream_compat.dart | 9 ++++- tool/lib/src/upstream_compat.dart | 29 +++++++++++---- .../package_not_on_pub_dev/published.yaml | 6 +++ .../unpublished_package/expect | 4 ++ .../unpublished_package/pub_dev.yaml | 2 + .../fixture_sqflite_watchos/pubspec.yaml | 8 ++++ .../unpublished_package/resolve.yaml | 6 +++ .../upstream_versions.yaml | 3 ++ tool/test/upstream_compat_test.dart | 37 ++++++++++++++++++- 10 files changed, 98 insertions(+), 10 deletions(-) create mode 100644 tool/test/fixtures/upstream_compat/package_not_on_pub_dev/published.yaml create mode 100644 tool/test/fixtures/upstream_compat/unpublished_package/expect create mode 100644 tool/test/fixtures/upstream_compat/unpublished_package/pub_dev.yaml create mode 100644 tool/test/fixtures/upstream_compat/unpublished_package/repo/packages/fixture_sqflite_watchos/pubspec.yaml create mode 100644 tool/test/fixtures/upstream_compat/unpublished_package/resolve.yaml create mode 100644 tool/test/fixtures/upstream_compat/unpublished_package/upstream_versions.yaml diff --git a/tool/README.md b/tool/README.md index 28b4081..39802e7 100644 --- a/tool/README.md +++ b/tool/README.md @@ -118,7 +118,9 @@ moved on: pub.dev with `upstream_versions.yaml`, and fails on a new major: check the package against it, then move the table and the README snippets together. It also resolves a scratch app with the latest upstream and the package, - once from this repository and once at its latest version on pub.dev. + once from this repository and once at its latest version on pub.dev. A + package without a row in `published.yaml` is staged and not on pub.dev + yet, so it resolves from this repository only. - `bin/pana_scores.dart` reads one `pana --json` report per package. Every section must be full, except "Platform support" for a package that declares only watchOS, which pana does not score (`AUTHORING.md` diff --git a/tool/bin/upstream_compat.dart b/tool/bin/upstream_compat.dart index bd59277..4a4292c 100644 --- a/tool/bin/upstream_compat.dart +++ b/tool/bin/upstream_compat.dart @@ -8,7 +8,7 @@ /// /// Usage, from `tool/`: `dart run bin/upstream_compat.dart /// [--root ] [--table ] -/// [--flutter ]`. +/// [--published ] [--flutter ]`. library; import 'dart:io'; @@ -17,11 +17,13 @@ import 'package:path/path.dart' as p; import 'package:plugins_tool/src/forbidden_words.dart'; import 'package:plugins_tool/src/repository.dart'; import 'package:plugins_tool/src/upstream_compat.dart'; +import 'package:plugins_tool/src/versions.dart'; Future main(List arguments) async { final Map options = parseOptions(arguments, { 'root', 'table', + 'published', 'flutter', }); final String tool = toolDirectory(); @@ -30,6 +32,11 @@ Future main(List arguments) async { exitCode = await runUpstreamCompatibility( options['root'] ?? repositoryRootOf(tool), options['table'] ?? p.join(tool, 'upstream_versions.yaml'), + parsePublished( + File( + options['published'] ?? p.join(tool, 'published.yaml'), + ).readAsStringSync(), + ), pubDevFetcher(client), flutterPubGet(flutter: options['flutter'] ?? 'flutter'), ForbiddenWords.load(p.join(tool, 'words', 'words.txt')), diff --git a/tool/lib/src/upstream_compat.dart b/tool/lib/src/upstream_compat.dart index 6aba739..a877288 100644 --- a/tool/lib/src/upstream_compat.dart +++ b/tool/lib/src/upstream_compat.dart @@ -16,6 +16,11 @@ /// - resolves a scratch app with the upstream at its latest version and the /// package from this repository, and another with the package's latest /// version on pub.dev, as a user would; either failing to resolve fails. +/// +/// A package without a row in `tool/published.yaml` is staged +/// (`AUTHORING.md` section 4) and not on pub.dev yet, so only the package +/// from this repository is resolved. A package with a row that pub.dev does +/// not know fails. library; import 'dart:convert'; @@ -28,6 +33,7 @@ import 'package:yaml/yaml.dart'; import 'forbidden_words.dart'; import 'public_log.dart'; import 'readme_snippets.dart'; +import 'versions.dart'; /// Returns a package's latest version on pub.dev, or null when pub.dev does /// not know the package. @@ -212,10 +218,12 @@ List resolutionReason(String output, {int limit = 8}) { } /// Runs the check on the repository at [root] with the table in -/// [tableFile], writes the report to [out] and returns the exit code. +/// [tableFile] and the rows of `tool/published.yaml` in [published], writes +/// the report to [out] and returns the exit code. Future runUpstreamCompatibility( String root, String tableFile, + Map published, LatestVersionFetcher latestVersion, Resolver resolve, ForbiddenWords words, @@ -287,9 +295,7 @@ Future runUpstreamCompatibility( packagePath: p.absolute(root, 'packages', package), ), ); - if (packageLatest == null) { - report.problems.add('pub.dev has no version of $package.'); - } else { + if (packageLatest != null) { tryResolve( 'published', '$packageLatest from pub.dev', @@ -300,11 +306,20 @@ Future runUpstreamCompatibility( packageVersion: packageLatest, ), ); + } else if (published.containsKey(package)) { + report.problems.add('pub.dev has no version of $package.'); + } else { + report.notes.add( + 'Not published yet (no row in tool/published.yaml), so only the ' + 'package from this repository was resolved.', + ); } } - report.summary = - '$upstream $upstreamLatest resolves with the package from this ' - 'repository and with $packageLatest from pub.dev'; + report.summary = packageLatest == null + ? '$upstream $upstreamLatest resolves with the package from this ' + 'repository' + : '$upstream $upstreamLatest resolves with the package from this ' + 'repository and with $packageLatest from pub.dev'; if (report.writeTo(out, words)) { failures++; } diff --git a/tool/test/fixtures/upstream_compat/package_not_on_pub_dev/published.yaml b/tool/test/fixtures/upstream_compat/package_not_on_pub_dev/published.yaml new file mode 100644 index 0000000..f14dff5 --- /dev/null +++ b/tool/test/fixtures/upstream_compat/package_not_on_pub_dev/published.yaml @@ -0,0 +1,6 @@ +# The package has a row, as if it were published, but pub.dev does not +# know it. This check reads only the package names. +packages: + fixture_battery_plus_watchos: + version: 0.1.0 + commit: '0000000000000000000000000000000000000000' diff --git a/tool/test/fixtures/upstream_compat/unpublished_package/expect b/tool/test/fixtures/upstream_compat/unpublished_package/expect new file mode 100644 index 0000000..cceb154 --- /dev/null +++ b/tool/test/fixtures/upstream_compat/unpublished_package/expect @@ -0,0 +1,4 @@ +0 +ok fixture_sqflite_watchos: sqflite 2.4.5 resolves with the package from this repository +Not published yet (no row in tool/published.yaml), so only the package from this repository was resolved. +Upstream compatibility: all 1 packages resolve with their latest upstream. diff --git a/tool/test/fixtures/upstream_compat/unpublished_package/pub_dev.yaml b/tool/test/fixtures/upstream_compat/unpublished_package/pub_dev.yaml new file mode 100644 index 0000000..2a00855 --- /dev/null +++ b/tool/test/fixtures/upstream_compat/unpublished_package/pub_dev.yaml @@ -0,0 +1,2 @@ +# latest.version on pub.dev, per package. +sqflite: 2.4.5 diff --git a/tool/test/fixtures/upstream_compat/unpublished_package/repo/packages/fixture_sqflite_watchos/pubspec.yaml b/tool/test/fixtures/upstream_compat/unpublished_package/repo/packages/fixture_sqflite_watchos/pubspec.yaml new file mode 100644 index 0000000..fcf4b6f --- /dev/null +++ b/tool/test/fixtures/upstream_compat/unpublished_package/repo/packages/fixture_sqflite_watchos/pubspec.yaml @@ -0,0 +1,8 @@ +name: fixture_sqflite_watchos +version: 0.0.1 +flutter: + plugin: + implements: sqflite + platforms: + watchos: + ffiPlugin: true diff --git a/tool/test/fixtures/upstream_compat/unpublished_package/resolve.yaml b/tool/test/fixtures/upstream_compat/unpublished_package/resolve.yaml new file mode 100644 index 0000000..2d71068 --- /dev/null +++ b/tool/test/fixtures/upstream_compat/unpublished_package/resolve.yaml @@ -0,0 +1,6 @@ +# What pub get prints when it fails, per package and source. The package +# has no row in published.yaml and pub.dev does not know it, so the check +# must not resolve it from pub.dev. +fixture_sqflite_watchos published: | + Resolving dependencies... + Because upstream_compatibility depends on fixture_sqflite_watchos any which doesn't exist (could not find package fixture_sqflite_watchos at https://pub.dev), version solving failed. diff --git a/tool/test/fixtures/upstream_compat/unpublished_package/upstream_versions.yaml b/tool/test/fixtures/upstream_compat/unpublished_package/upstream_versions.yaml new file mode 100644 index 0000000..ea4353c --- /dev/null +++ b/tool/test/fixtures/upstream_compat/unpublished_package/upstream_versions.yaml @@ -0,0 +1,3 @@ +upstreams: + sqflite: 2.4.5 +exceptions: {} diff --git a/tool/test/upstream_compat_test.dart b/tool/test/upstream_compat_test.dart index 98ab372..b98628f 100644 --- a/tool/test/upstream_compat_test.dart +++ b/tool/test/upstream_compat_test.dart @@ -4,7 +4,8 @@ // The weekly upstream compatibility check on its fixtures, in // tool/test/fixtures/upstream_compat/. Each fixture's pub_dev.yaml stands -// in for the pub.dev API, and its resolve.yaml for `flutter pub get`: a +// in for the pub.dev API, its published.yaml (when it has one) for +// tool/published.yaml, and its resolve.yaml for `flutter pub get`: a // package and source listed there fails to resolve with that output. The // games_services output is the one plugins.md P6 recorded on 2026-09-29. @@ -14,6 +15,7 @@ import 'dart:io'; import 'package:path/path.dart' as p; import 'package:plugins_tool/src/forbidden_words.dart'; import 'package:plugins_tool/src/upstream_compat.dart'; +import 'package:plugins_tool/src/versions.dart'; import 'package:pub_semver/pub_semver.dart'; import 'package:test/test.dart'; import 'package:yaml/yaml.dart'; @@ -40,6 +42,14 @@ LatestVersionFetcher fixtureFetcher(String fixture) { }); } +/// The rows of the fixture's published.yaml; none when it has none. +Map fixturePublished(String fixture) { + final File published = File(p.join(fixture, 'published.yaml')); + return published.existsSync() + ? parsePublished(published.readAsStringSync()) + : {}; +} + /// A resolver that fails for the package and source the fixture's /// resolve.yaml lists, and checks each scratch pubspec it is given. Resolver fixtureResolver(String fixture, String root) { @@ -100,6 +110,7 @@ void main() { final int code = await runUpstreamCompatibility( root, p.join(fixture.directory, 'upstream_versions.yaml'), + fixturePublished(fixture.directory), fixtureFetcher(fixture.directory), fixtureResolver(fixture.directory, root), words, @@ -124,6 +135,7 @@ void main() { final int code = await runUpstreamCompatibility( root, p.join(fixture, 'upstream_versions.yaml'), + fixturePublished(fixture), mapFetcher({ 'battery_plus': '7.1.1', 'fixture_battery_plus_watchos': '0.1.0-${words.words.first}.1', @@ -149,6 +161,7 @@ void main() { final int code = await runUpstreamCompatibility( root, p.join(fixture, 'upstream_versions.yaml'), + fixturePublished(fixture), (String package) async => throw const HttpException('pub.dev answered 503.'), fixtureResolver(fixture, root), @@ -159,6 +172,28 @@ void main() { expect('$out', contains('pub.dev could not be read')); }); + test('an unpublished package does not claim a version on pub.dev', () async { + final String fixture = p.join( + 'test', + 'fixtures', + 'upstream_compat', + 'unpublished_package', + ); + final String root = p.join(fixture, 'repo'); + final StringBuffer out = StringBuffer(); + final int code = await runUpstreamCompatibility( + root, + p.join(fixture, 'upstream_versions.yaml'), + fixturePublished(fixture), + fixtureFetcher(fixture), + fixtureResolver(fixture, root), + words, + out, + ); + expect(code, 0, reason: '$out'); + expect('$out', isNot(contains('from pub.dev'))); + }); + test('compareWithTable', () { final Version table = Version.parse('7.1.1'); expect(compareWithTable('b', table, Version.parse('7.1.1')), (