From 52f512315bc886de487061091ef82056abf9dab8 Mon Sep 17 00:00:00 2001 From: Sigurd Meldgaard Date: Thu, 25 Sep 2025 16:16:04 +0200 Subject: [PATCH] Handle all examples in workspace with flag `--example` (#4679) --- lib/src/command/add.dart | 16 +-- lib/src/command/downgrade.dart | 30 +++--- lib/src/command/get.dart | 19 ++-- lib/src/command/remove.dart | 15 +-- lib/src/command/upgrade.dart | 36 ++++--- lib/src/entrypoint.dart | 43 +++++--- ... not update major versions in example~.txt | 2 +- test/workspace_test.dart | 98 +++++++++++++++++++ 8 files changed, 196 insertions(+), 63 deletions(-) diff --git a/lib/src/command/add.dart b/lib/src/command/add.dart index 7c6c701dc3..e6d6e54978 100644 --- a/lib/src/command/add.dart +++ b/lib/src/command/add.dart @@ -291,14 +291,14 @@ Specify multiple sdk packages with descriptors.'''); precompile: !argResults.isDryRun && argResults.shouldPrecompile, ); - if (!argResults.isDryRun && - argResults.example && - entrypoint.example != null) { - await entrypoint.example!.acquireDependencies( - SolveType.get, - precompile: argResults.shouldPrecompile, - summaryOnly: true, - ); + if (!argResults.isDryRun && argResults.example) { + for (final example in entrypoint.examples) { + await example.acquireDependencies( + SolveType.get, + precompile: argResults.shouldPrecompile, + summaryOnly: true, + ); + } } if (isOffline) { diff --git a/lib/src/command/downgrade.dart b/lib/src/command/downgrade.dart index 1bdd09230a..cd903764f1 100644 --- a/lib/src/command/downgrade.dart +++ b/lib/src/command/downgrade.dart @@ -81,21 +81,27 @@ class DowngradeCommand extends PubCommand { unlock: argResults.rest, dryRun: _dryRun, ); - final example = entrypoint.example; - if (argResults.flag('example') && example != null) { - await example.acquireDependencies( - SolveType.get, - unlock: argResults.rest, - dryRun: _dryRun, - summaryOnly: true, - ); + if (_example) { + for (final example in entrypoint.examples) { + await example.acquireDependencies( + SolveType.get, + unlock: argResults.rest, + dryRun: _dryRun, + summaryOnly: true, + ); + } } if (_tighten) { - if (_example && entrypoint.example != null) { - log.warning( - 'Running `downgrade --tighten` only in `${entrypoint.workspaceRoot.dir}`. Run `$topLevelProgram pub upgrade --tighten --directory example/` separately.', - ); + if (_example && entrypoint.examples.isNotEmpty) { + for (final example in entrypoint.examples) { + log.warning( + 'Running `downgrade --tighten` only in ' + '`${entrypoint.workspaceRoot.dir}`. ' + 'Run `$topLevelProgram pub downgrade --tighten ' + '--directory ${example.workspaceRoot.presentationDir}` separately.', + ); + } } final changes = entrypoint.tighten(); entrypoint.applyChanges(changes, _dryRun); diff --git a/lib/src/command/get.dart b/lib/src/command/get.dart index 4e522c05e7..978d6f6b8a 100644 --- a/lib/src/command/get.dart +++ b/lib/src/command/get.dart @@ -84,15 +84,16 @@ class GetCommand extends PubCommand { enforceLockfile: argResults.flag('enforce-lockfile'), ); - final example = entrypoint.example; - if ((argResults.flag('example')) && example != null) { - await example.acquireDependencies( - SolveType.get, - dryRun: argResults.flag('dry-run'), - precompile: argResults.flag('precompile'), - summaryOnly: true, - enforceLockfile: argResults.flag('enforce-lockfile'), - ); + if (argResults.flag('example')) { + for (final example in entrypoint.examples) { + await example.acquireDependencies( + SolveType.get, + dryRun: argResults.flag('dry-run'), + precompile: argResults.flag('precompile'), + summaryOnly: true, + enforceLockfile: argResults.flag('enforce-lockfile'), + ); + } } } } diff --git a/lib/src/command/remove.dart b/lib/src/command/remove.dart index 32e75906b3..df648e96ca 100644 --- a/lib/src/command/remove.dart +++ b/lib/src/command/remove.dart @@ -98,13 +98,14 @@ To remove a dependency override of a package prefix the package name with dryRun: isDryRun, ); - final example = entrypoint.example; - if (!isDryRun && argResults.flag('example') && example != null) { - await example.acquireDependencies( - SolveType.get, - precompile: argResults.flag('precompile'), - summaryOnly: true, - ); + if (!isDryRun && argResults.flag('example')) { + for (final example in entrypoint.examples) { + await example.acquireDependencies( + SolveType.get, + precompile: argResults.flag('precompile'), + summaryOnly: true, + ); + } } } diff --git a/lib/src/command/upgrade.dart b/lib/src/command/upgrade.dart index a817ff41e0..0c25686d8b 100644 --- a/lib/src/command/upgrade.dart +++ b/lib/src/command/upgrade.dart @@ -155,19 +155,30 @@ Consider using the Dart 2.19 sdk to migrate to null safety.'''); } if (_upgradeMajorVersions) { - if (argResults.flag('example') && entrypoint.example != null) { - log.warning( - 'Running `upgrade --major-versions` only in `${entrypoint.workspaceRoot.dir}`. Run `$topLevelProgram pub upgrade --major-versions --directory example/` separately.', - ); + if (argResults.flag('example')) { + for (final example in entrypoint.examples) { + log.warning( + 'Running `upgrade --major-versions` only in ' + '`${entrypoint.workspaceRoot.dir}`. ' + 'Run `$topLevelProgram pub upgrade --major-versions ' + '--directory ${example.workspaceRoot.presentationDir}` separately.', + ); + } } await _runUpgradeMajorVersions(); } else { await _runUpgrade(entrypoint); if (_tighten) { - if (argResults.flag('example') && entrypoint.example != null) { - log.warning( - 'Running `upgrade --tighten` only in `${entrypoint.workspaceRoot.dir}`. Run `$topLevelProgram pub upgrade --tighten --directory example/` separately.', - ); + if (argResults.flag('example')) { + for (final example in entrypoint.examples) { + log.warning( + 'Running `upgrade --tighten` only in ' + '`${entrypoint.workspaceRoot.dir}`. ' + 'Run `$topLevelProgram pub upgrade --tighten ' + '--directory ${example.workspaceRoot.presentationDir}` ' + 'separately.', + ); + } } final changes = entrypoint.tighten( packagesToUpgrade: await _packagesToUpgrade, @@ -175,11 +186,10 @@ Consider using the Dart 2.19 sdk to migrate to null safety.'''); entrypoint.applyChanges(changes, _dryRun); } } - if (argResults.flag('example') && entrypoint.example != null) { - // Reload the entrypoint to ensure we pick up potential changes that has - // been made. - final exampleEntrypoint = Entrypoint(directory, cache).example!; - await _runUpgrade(exampleEntrypoint, onlySummary: true); + if (argResults.flag('example')) { + for (final example in entrypoint.examples) { + await _runUpgrade(example, onlySummary: true); + } } } diff --git a/lib/src/entrypoint.dart b/lib/src/entrypoint.dart index 3f2be96950..2eda882e9b 100644 --- a/lib/src/entrypoint.dart +++ b/lib/src/entrypoint.dart @@ -321,7 +321,7 @@ See $workspacesDocUrl for more information.''', Entrypoint._( this.workingDir, this._lockFile, - this._example, + this._examples, this._packageGraph, this.cache, this._packages, @@ -349,10 +349,15 @@ See $workspacesDocUrl for more information.''', final newWorkPackage = newWorkspaceRoot.transitiveWorkspace.firstWhere( (package) => package.dir == workPackage.dir, ); - return Entrypoint._(workingDir, _lockFile, _example, _packageGraph, cache, ( - root: newWorkspaceRoot, - work: newWorkPackage, - ), isCachedGlobal); + return Entrypoint._( + workingDir, + _lockFile, + _examples, + _packageGraph, + cache, + (root: newWorkspaceRoot, work: newWorkPackage), + isCachedGlobal, + ); } /// Creates an entrypoint at the same location, that will use [pubspec] for @@ -378,18 +383,30 @@ See $workspacesDocUrl for more information.''', } } - /// Gets the [Entrypoint] package for the current working directory. + /// Gets [Entrypoint]s for examples of any workspace packages. /// - /// This will be null if the example folder doesn't have a `pubspec.yaml`. - Entrypoint? get example { - if (_example != null) return _example; - if (!fileExists(workspaceRoot.path('example', 'pubspec.yaml'))) { - return null; + /// Does not return examples that are already in the workspace + /// + /// This will be empty if the example folder doesn't have a `pubspec.yaml`. + List get examples { + if (_examples case final List examples) return examples; + final directoriesInWorkspace = {}; + for (final package in workspaceRoot.transitiveWorkspace) { + directoriesInWorkspace.add(p.canonicalize(package.dir)); + } + final result = []; + for (final package in workspaceRoot.transitiveWorkspace) { + final examplePath = package.path('example'); + + if (!directoriesInWorkspace.contains(p.canonicalize(examplePath)) && + fileExists(p.join(examplePath, 'pubspec.yaml'))) { + result.add(Entrypoint(examplePath, cache)); + } } - return _example = Entrypoint(workspaceRoot.path('example'), cache); + return _examples = result; } - Entrypoint? _example; + List? _examples; /// Writes the .dart_tool/package_config.json file and workspace references to /// it. diff --git a/test/testdata/goldens/upgrade/example_warns_about_major_versions_test/pub upgrade --major-versions does not update major versions in example~.txt b/test/testdata/goldens/upgrade/example_warns_about_major_versions_test/pub upgrade --major-versions does not update major versions in example~.txt index 0fb9eb7a75..708e8b3b17 100644 --- a/test/testdata/goldens/upgrade/example_warns_about_major_versions_test/pub upgrade --major-versions does not update major versions in example~.txt +++ b/test/testdata/goldens/upgrade/example_warns_about_major_versions_test/pub upgrade --major-versions does not update major versions in example~.txt @@ -12,7 +12,7 @@ Changed 1 dependency! Resolving dependencies in `./example`... Downloading packages... Got dependencies in `./example`. -[STDERR] Running `upgrade --major-versions` only in `.`. Run `dart pub upgrade --major-versions --directory example/` separately. +[STDERR] Running `upgrade --major-versions` only in `.`. Run `dart pub upgrade --major-versions --directory ./example` separately. -------------------------------- END OF OUTPUT --------------------------------- diff --git a/test/workspace_test.dart b/test/workspace_test.dart index b348f8b20b..ff9fa35874 100644 --- a/test/workspace_test.dart +++ b/test/workspace_test.dart @@ -1725,6 +1725,104 @@ b a${s}b$s output: contains('+ bar'), ); }); + + test('`--example` gets all (non-workspace) examples in workspace', () async { + final server = await servePackages(); + server.serve('foo', '1.0.0'); + server.serve('foo', '1.5.0'); + + await dir(appPath, [ + libPubspec( + 'myapp', + '1.2.3', + extras: { + 'workspace': ['pkgs/a'], + }, + sdk: '^3.5.0', + ), + dir('pkgs', [ + dir('a', [ + libPubspec('a', '1.0.0', resolutionWorkspace: true), + dir('example', [libPubspec('example_b', '1.0.0')]), + ]), + dir('b', [ + libPubspec( + 'b', + '1.0.0', + resolutionWorkspace: true, + extras: { + 'workspace': ['example'], + }, + ), + dir('example', [libPubspec('example_b', '1.0.0')]), + ]), + ]), + ]).create(); + + final s = p.separator; + + await runPub( + args: ['get', '--example'], + environment: {'_PUB_TEST_SDK_VERSION': '3.5.0'}, + output: allOf( + contains('Got dependencies in `.${s}pkgs/a${s}example`.'), + isNot(contains('Got dependencies in `.${s}pkgs/b${s}example`.`.')), + ), + ); + + await runPub( + args: ['upgrade', '--example'], + environment: {'_PUB_TEST_SDK_VERSION': '3.5.0'}, + output: allOf( + contains('Got dependencies in `.${s}pkgs/a${s}example`.'), + isNot(contains('Got dependencies in `.${s}pkgs/b${s}example`.`.')), + ), + ); + + await runPub( + args: ['upgrade', '--example', '--tighten'], + environment: {'_PUB_TEST_SDK_VERSION': '3.5.0'}, + output: allOf( + contains('Got dependencies in `.${s}pkgs/a${s}example`.'), + isNot(contains('Got dependencies in `.${s}pkgs/b${s}example`.`.')), + ), + error: contains( + 'Running `upgrade --tighten` only in `.`. Run `dart pub upgrade --tighten --directory .${s}pkgs/a${s}example` separately.', + ), + ); + + await runPub( + args: ['upgrade', '--example', '--major-versions'], + environment: {'_PUB_TEST_SDK_VERSION': '3.5.0'}, + output: allOf( + contains('Got dependencies in `.${s}pkgs/a${s}example`.'), + isNot(contains('Got dependencies in `.${s}pkgs/b${s}example`.')), + ), + error: contains( + 'Running `upgrade --major-versions` only in `.`. Run `dart pub upgrade --major-versions --directory .${s}pkgs/a${s}example` separately.', + ), + ); + + await runPub( + args: ['add', 'foo:^1.0.0', '--example'], + environment: {'_PUB_TEST_SDK_VERSION': '3.5.0'}, + output: allOf( + contains('+ foo 1.5.0'), + contains('Got dependencies in `.${s}pkgs/a${s}example`.'), + isNot(contains('Got dependencies in `.${s}pkgs/b${s}example`.')), + ), + ); + + await runPub( + args: ['downgrade', '--example'], + environment: {'_PUB_TEST_SDK_VERSION': '3.5.0'}, + output: allOf( + contains('< foo 1.0.0'), + contains('Got dependencies in `.${s}pkgs/a${s}example`.'), + isNot(contains('Got dependencies in `.${s}pkgs/b${s}example`.`.')), + ), + ); + }); } final s = p.separator;