Handle all examples in workspace with flag `--example` (#4679)

diff --git a/lib/src/command/add.dart b/lib/src/command/add.dart
index 273c66f..23ba02b 100644
--- a/lib/src/command/add.dart
+++ b/lib/src/command/add.dart
@@ -290,14 +290,14 @@
           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 d999138..1dccee6 100644
--- a/lib/src/command/downgrade.dart
+++ b/lib/src/command/downgrade.dart
@@ -80,21 +80,27 @@
       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 dd3bbdc..9c08444 100644
--- a/lib/src/command/get.dart
+++ b/lib/src/command/get.dart
@@ -83,15 +83,16 @@
       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 7f4ac2a..5a68ca3 100644
--- a/lib/src/command/remove.dart
+++ b/lib/src/command/remove.dart
@@ -97,13 +97,14 @@
           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 44365e8..7078ac0 100644
--- a/lib/src/command/upgrade.dart
+++ b/lib/src/command/upgrade.dart
@@ -154,19 +154,30 @@
     }
 
     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,
@@ -174,11 +185,10 @@
         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 24d25dc..62dfa78 100644
--- a/lib/src/entrypoint.dart
+++ b/lib/src/entrypoint.dart
@@ -321,7 +321,7 @@
   Entrypoint._(
     this.workingDir,
     this._lockFile,
-    this._example,
+    this._examples,
     this._packageGraph,
     this.cache,
     this._packages,
@@ -349,10 +349,15 @@
     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 @@
     }
   }
 
-  /// 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<Entrypoint> get examples {
+    if (_examples case final List<Entrypoint> examples) return examples;
+    final directoriesInWorkspace = <String>{};
+    for (final package in workspaceRoot.transitiveWorkspace) {
+      directoriesInWorkspace.add(p.canonicalize(package.dir));
     }
-    return _example = Entrypoint(workspaceRoot.path('example'), cache);
+    final result = <Entrypoint>[];
+    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 _examples = result;
   }
 
-  Entrypoint? _example;
+  List<Entrypoint>? _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 0fb9eb7..708e8b3 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 @@
 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 b348f8b..ff9fa35 100644
--- a/test/workspace_test.dart
+++ b/test/workspace_test.dart
@@ -1725,6 +1725,104 @@
       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;