diff --git a/lib/src/authentication/credential.dart b/lib/src/authentication/credential.dart
index e1dfc79..010d3f3 100644
--- a/lib/src/authentication/credential.dart
+++ b/lib/src/authentication/credential.dart
@@ -58,7 +58,7 @@
     /// doesn't contains [key].
     ///
     /// Throws [FormatException] if value type is not [String].
-    String? _string(String key) {
+    String? string(String key) {
       if (json.containsKey(key)) {
         if (json[key] is! String) {
           throw FormatException('Provided $key value should be string');
@@ -71,8 +71,8 @@
     return Credential._internal(
       url: hostedUrl,
       unknownFields: unknownFields,
-      token: _string('token'),
-      env: _string('env'),
+      token: string('token'),
+      env: string('env'),
     );
   }
 
diff --git a/lib/src/authentication/token_store.dart b/lib/src/authentication/token_store.dart
index 3ef948d..fbb208a 100644
--- a/lib/src/authentication/token_store.dart
+++ b/lib/src/authentication/token_store.dart
@@ -112,31 +112,31 @@
 
   /// Adds [token] into store and writes into disk.
   void addCredential(Credential token) {
-    final _credentials = _loadCredentials();
+    final credentials = _loadCredentials();
 
     // Remove duplicate tokens
-    _credentials.removeWhere((it) => it.url == token.url);
-    _credentials.add(token);
-    _saveCredentials(_credentials);
+    credentials.removeWhere((it) => it.url == token.url);
+    credentials.add(token);
+    _saveCredentials(credentials);
   }
 
   /// Removes tokens with matching [hostedUrl] from store. Returns whether or
   /// not there's a stored token with matching url.
   bool removeCredential(Uri hostedUrl) {
-    final _credentials = _loadCredentials();
+    final credentials = _loadCredentials();
 
     var i = 0;
     var found = false;
-    while (i < _credentials.length) {
-      if (_credentials[i].url == hostedUrl) {
-        _credentials.removeAt(i);
+    while (i < credentials.length) {
+      if (credentials[i].url == hostedUrl) {
+        credentials.removeAt(i);
         found = true;
       } else {
         i++;
       }
     }
 
-    _saveCredentials(_credentials);
+    _saveCredentials(credentials);
 
     return found;
   }
diff --git a/lib/src/command.dart b/lib/src/command.dart
index ee62392..a1a8a2e 100644
--- a/lib/src/command.dart
+++ b/lib/src/command.dart
@@ -73,7 +73,12 @@
   ///
   /// This will load the pubspec and fail with an error if the current directory
   /// is not a package.
-  late final Entrypoint entrypoint = Entrypoint(directory, cache);
+  late final Entrypoint entrypoint =
+      Entrypoint(directory, cache, withPubspecOverrides: withPubspecOverrides);
+
+  /// Whether `pubspec_overrides.yaml` is taken into account, when creating
+  /// [entrypoint].
+  bool get withPubspecOverrides => true;
 
   /// The URL for web documentation for this command.
   String? get docUrl => null;
@@ -293,7 +298,7 @@
   ///
   /// For top-level commands, if an alias is used, the primary command name is
   /// returned. For instance `install` becomes `get`.
-  static late final String command = _command ?? '';
+  static final String command = _command ?? '';
 
   static void computeCommand(ArgResults argResults) {
     var list = <String?>[];
diff --git a/lib/src/command/add.dart b/lib/src/command/add.dart
index 0980b81..d4c98ef 100644
--- a/lib/src/command/add.dart
+++ b/lib/src/command/add.dart
@@ -94,6 +94,8 @@
         help: 'Build executables in immediate dependencies.');
     argParser.addOption('directory',
         abbr: 'C', help: 'Run this in the directory <dir>.', valueHelp: 'dir');
+    argParser.addFlag('legacy-packages-file',
+        help: 'Generate the legacy ".packages" file', negatable: false);
   }
 
   @override
@@ -165,7 +167,8 @@
           .acquireDependencies(SolveType.get,
               dryRun: true,
               precompile: argResults['precompile'],
-              analytics: analytics);
+              analytics: analytics,
+              generateDotPackages: false);
     } else {
       /// Update the `pubspec.yaml` before calling [acquireDependencies] to
       /// ensure that the modification timestamp on `pubspec.lock` and
@@ -180,6 +183,7 @@
         SolveType.get,
         precompile: argResults['precompile'],
         analytics: analytics,
+        generateDotPackages: argResults['legacy-packages-file'],
       );
 
       if (argResults['example'] && entrypoint.example != null) {
@@ -188,6 +192,7 @@
           precompile: argResults['precompile'],
           onlyReportSuccessOrFailure: true,
           analytics: analytics,
+          generateDotPackages: argResults['legacy-packages-file'],
         );
       }
     }
@@ -286,7 +291,7 @@
   /// If any of the other git options are defined when `--git-url` is not
   /// defined, an error will be thrown.
   _ParseResult _parsePackage(String package, LanguageVersion languageVersion) {
-    final _conflictingFlagSets = [
+    final conflictingFlagSets = [
       ['git-url', 'git-ref', 'git-path'],
       ['hosted-url'],
       ['path'],
@@ -294,8 +299,8 @@
     ];
 
     for (final flag
-        in _conflictingFlagSets.expand((s) => s).where(argResults.wasParsed)) {
-      final conflictingFlag = _conflictingFlagSets
+        in conflictingFlagSets.expand((s) => s).where(argResults.wasParsed)) {
+      final conflictingFlag = conflictingFlagSets
           .where((s) => !s.contains(flag))
           .expand((s) => s)
           .firstWhereOrNull(argResults.wasParsed);
@@ -400,17 +405,23 @@
             'version': versionConstraintString
         };
       }
-      final packagePath = [dependencyKey, name];
 
       if (yamlEditor.parseAt(
             [dependencyKey],
             orElse: () => YamlScalar.wrap(null),
           ).value ==
           null) {
-        // Insert dependencyKey: {} if it did not exist.
-        yamlEditor.update([dependencyKey], {});
+        // Handle the case where [dependencyKey] does not already exist.
+        // We ensure it is in Block-style by default.
+        yamlEditor.update(
+            [dependencyKey],
+            wrapAsYamlNode({name: pubspecInformation},
+                collectionStyle: CollectionStyle.BLOCK));
+      } else {
+        final packagePath = [dependencyKey, name];
+
+        yamlEditor.update(packagePath, pubspecInformation);
       }
-      yamlEditor.update(packagePath, pubspecInformation);
 
       /// Remove the package from dev_dependencies if we are adding it to
       /// dependencies. Refer to [_addPackageToPubspec] for additional discussion.
diff --git a/lib/src/command/cache_repair.dart b/lib/src/command/cache_repair.dart
index 850b782..a4dccab 100644
--- a/lib/src/command/cache_repair.dart
+++ b/lib/src/command/cache_repair.dart
@@ -66,10 +66,10 @@
     if (globalRepairResults.last.isNotEmpty) {
       var packages = pluralize('package', globalRepairResults.last.length);
       log.message(
-          'Failed to reactivate ${log.red(globalRepairResults.last.length)} $packages:\n' +
-              globalRepairResults.last
-                  .map((name) => '- ${log.bold(name)}')
-                  .join('\n'));
+          'Failed to reactivate ${log.red(globalRepairResults.last.length)} $packages:');
+      log.message(globalRepairResults.last
+          .map((name) => '- ${log.bold(name)}')
+          .join('\n'));
     }
 
     if (successes.isEmpty && failures.isEmpty) {
diff --git a/lib/src/command/dependency_services.dart b/lib/src/command/dependency_services.dart
index 19cc867..193f345 100644
--- a/lib/src/command/dependency_services.dart
+++ b/lib/src/command/dependency_services.dart
@@ -72,7 +72,7 @@
     final dependencies = <Object>[];
     final result = <String, Object>{'dependencies': dependencies};
 
-    Future<List<Object>> _computeUpgradeSet(
+    Future<List<Object>> computeUpgradeSet(
       Pubspec rootPubspec,
       PackageId? package, {
       required _UpgradeType upgradeType,
@@ -203,15 +203,15 @@
                 ?.versionOrHash(),
         'constraint':
             _constraintOf(compatiblePubspec, package.name)?.toString(),
-        'compatible': await _computeUpgradeSet(
+        'compatible': await computeUpgradeSet(
             compatiblePubspec, compatibleVersion,
             upgradeType: _UpgradeType.compatible),
         'singleBreaking': kind != 'transitive' && singleBreakingVersion == null
             ? []
-            : await _computeUpgradeSet(compatiblePubspec, singleBreakingVersion,
+            : await computeUpgradeSet(compatiblePubspec, singleBreakingVersion,
                 upgradeType: _UpgradeType.singleBreaking),
         'multiBreaking': kind != 'transitive' && multiBreakingVersion != null
-            ? await _computeUpgradeSet(compatiblePubspec, multiBreakingVersion,
+            ? await computeUpgradeSet(compatiblePubspec, multiBreakingVersion,
                 upgradeType: _UpgradeType.multiBreaking)
             : [],
       });
@@ -281,7 +281,7 @@
     final dependencies = <Object>[];
     final result = <String, Object>{'dependencies': dependencies};
 
-    for (final package in currentPackages) {
+    for (final package in currentPackages.where((p) => !p.isRoot)) {
       dependencies.add({
         'name': package.name,
         'version': package.versionOrHash(),
diff --git a/lib/src/command/downgrade.dart b/lib/src/command/downgrade.dart
index 274ab48..fce5ef3 100644
--- a/lib/src/command/downgrade.dart
+++ b/lib/src/command/downgrade.dart
@@ -42,6 +42,8 @@
 
     argParser.addOption('directory',
         abbr: 'C', help: 'Run this in the directory<dir>.', valueHelp: 'dir');
+    argParser.addFlag('legacy-packages-file',
+        help: 'Generate the legacy ".packages" file', negatable: false);
   }
 
   @override
@@ -57,6 +59,7 @@
       unlock: argResults.rest,
       dryRun: dryRun,
       analytics: analytics,
+      generateDotPackages: argResults['legacy-packages-file'],
     );
     var example = entrypoint.example;
     if (argResults['example'] && example != null) {
@@ -66,6 +69,7 @@
         dryRun: dryRun,
         onlyReportSuccessOrFailure: true,
         analytics: analytics,
+        generateDotPackages: argResults['legacy-packages-file'],
       );
     }
 
diff --git a/lib/src/command/get.dart b/lib/src/command/get.dart
index 302bb93..7b8906c 100644
--- a/lib/src/command/get.dart
+++ b/lib/src/command/get.dart
@@ -33,6 +33,9 @@
 
     argParser.addFlag('packages-dir', hide: true);
 
+    argParser.addFlag('legacy-packages-file',
+        help: 'Generate the legacy ".packages" file', negatable: false);
+
     argParser.addFlag(
       'example',
       help: 'Also run in `example/` (if it exists).',
@@ -53,16 +56,20 @@
       SolveType.get,
       dryRun: argResults['dry-run'],
       precompile: argResults['precompile'],
+      generateDotPackages: argResults['legacy-packages-file'],
       analytics: analytics,
     );
 
     var example = entrypoint.example;
     if (argResults['example'] && example != null) {
-      await example.acquireDependencies(SolveType.get,
-          dryRun: argResults['dry-run'],
-          precompile: argResults['precompile'],
-          onlyReportSuccessOrFailure: true,
-          analytics: analytics);
+      await example.acquireDependencies(
+        SolveType.get,
+        dryRun: argResults['dry-run'],
+        precompile: argResults['precompile'],
+        generateDotPackages: argResults['legacy-packages-file'],
+        analytics: analytics,
+        onlyReportSuccessOrFailure: true,
+      );
     }
   }
 }
diff --git a/lib/src/command/lish.dart b/lib/src/command/lish.dart
index a20fbdf..69c1c7e 100644
--- a/lib/src/command/lish.dart
+++ b/lib/src/command/lish.dart
@@ -32,6 +32,8 @@
   String get docUrl => 'https://dart.dev/tools/pub/cmd/pub-lish';
   @override
   bool get takesArguments => false;
+  @override
+  bool get withPubspecOverrides => false;
 
   /// The URL of the server to which to upload the package.
   late final Uri host = () {
@@ -131,7 +133,7 @@
             '    pub token add $host\n';
       }
       if (error.serverMessage != null) {
-        msg += '\n' + error.serverMessage! + '\n';
+        msg += '\n${error.serverMessage!}\n';
       }
       dataError(msg + log.red('Authentication failed!'));
     } on PubHttpException catch (error) {
@@ -169,8 +171,8 @@
       };
 
       // Using OAuth2 authentication client for the official pub servers
-      final isOfficalServer = officialPubServers.contains(host.toString());
-      if (isOfficalServer && !cache.tokenStore.hasCredential(host)) {
+      final isOfficialServer = officialPubServers.contains(host.toString());
+      if (isOfficialServer && !cache.tokenStore.hasCredential(host)) {
         // Using OAuth2 authentication client for the official pub servers, when
         // we don't have an explicit token from [TokenStore] to use instead.
         //
@@ -222,8 +224,10 @@
 
     // Show the package contents so the user can verify they look OK.
     var package = entrypoint.root;
-    log.message('Publishing ${package.name} ${package.version} to $host:\n'
-        '${tree.fromFiles(files, baseDir: entrypoint.root.dir)}');
+    log.message(
+      'Publishing ${package.name} ${package.version} to $host:\n'
+      '${tree.fromFiles(files, baseDir: entrypoint.root.dir, showAllChildren: true)}',
+    );
 
     var packageBytesFuture =
         createTarGz(files, baseDir: entrypoint.root.dir).toBytes();
@@ -292,7 +296,8 @@
         '\nPolicy details are available at https://pub.dev/policy');
 
     final package = entrypoint.root;
-    var message = 'Do you want to publish ${package.name} ${package.version}';
+    var message =
+        'Do you want to publish ${package.name} ${package.version} to $host';
 
     if (warnings.isNotEmpty || hints.isNotEmpty) {
       final warning = formatWarningCount();
diff --git a/lib/src/command/login.dart b/lib/src/command/login.dart
index 8c31e69..96c04d8 100644
--- a/lib/src/command/login.dart
+++ b/lib/src/command/login.dart
@@ -6,6 +6,7 @@
 import 'dart:convert';
 
 import '../command.dart';
+import '../command_runner.dart';
 import '../http.dart';
 import '../log.dart' as log;
 import '../oauth2.dart' as oauth2;
@@ -26,12 +27,17 @@
     final credentials = oauth2.loadCredentials(cache);
     if (credentials == null) {
       final userInfo = await _retrieveUserInfo();
-      log.message('You are now logged in as $userInfo');
+      if (userInfo == null) {
+        log.warning('Could not retrieve your user-details.\n'
+            'You might have to run `$topLevelProgram pub logout` to delete your credentials and try again.');
+      } else {
+        log.message('You are now logged in as $userInfo');
+      }
     } else {
       final userInfo = await _retrieveUserInfo();
       if (userInfo == null) {
         log.warning('Your credentials seems broken.\n'
-            'Run `pub logout` to delete your credentials  and try again.');
+            'Run `$topLevelProgram pub logout` to delete your credentials and try again.');
       }
       log.warning('You are already logged in as $userInfo\n'
           'Run `pub logout` to log out and try again.');
@@ -47,8 +53,18 @@
       if (userInfoRequest.statusCode != 200) return null;
       try {
         final userInfo = json.decode(userInfoRequest.body);
-        return _UserInfo(userInfo['name'], userInfo['email']);
-      } on FormatException {
+        final name = userInfo['name'];
+        final email = userInfo['email'];
+        if (email is String) {
+          return _UserInfo(name, email);
+        } else {
+          log.fine(
+              'Bad response from $userInfoEndpoint: ${userInfoRequest.body}');
+          return null;
+        }
+      } on FormatException catch (e) {
+        log.fine(
+            'Bad response from $userInfoEndpoint ($e): ${userInfoRequest.body}');
         return null;
       }
     });
@@ -56,9 +72,9 @@
 }
 
 class _UserInfo {
-  final String name;
+  final String? name;
   final String email;
   _UserInfo(this.name, this.email);
   @override
-  String toString() => ['<$email>', name].join(' ');
+  String toString() => ['<$email>', name ?? ''].join(' ');
 }
diff --git a/lib/src/command/outdated.dart b/lib/src/command/outdated.dart
index 5c275e4..601bdc8 100644
--- a/lib/src/command/outdated.dart
+++ b/lib/src/command/outdated.dart
@@ -114,7 +114,7 @@
 
   @override
   Future<void> runProtected() async {
-    final mode = <String, Mode>{
+    final mode = <String, _Mode>{
       'outdated': _OutdatedMode(),
       'null-safety': _NullSafetyMode(cache, entrypoint,
           shouldShowSpinner: _shouldShowSpinner),
@@ -385,7 +385,7 @@
 
 Future<void> _outputJson(
   List<_PackageDetails> rows,
-  Mode mode, {
+  _Mode mode, {
   required bool showAll,
   required bool includeDevDependencies,
 }) async {
@@ -422,7 +422,7 @@
 
 Future<void> _outputHuman(
   List<_PackageDetails> rows,
-  Mode mode, {
+  _Mode mode, {
   required bool showAll,
   required bool useColors,
   required bool includeDevDependencies,
@@ -435,7 +435,7 @@
   required String directory,
 }) async {
   final directoryDesc = directory == '.' ? '' : ' in $directory';
-  log.message(mode.explanation(directoryDesc) + '\n');
+  log.message('${mode.explanation(directoryDesc)}\n');
   final markedRows =
       Map.fromIterables(rows, await mode.markVersionDetails(rows));
 
@@ -596,7 +596,7 @@
   }
 }
 
-abstract class Mode {
+abstract class _Mode {
   /// Analyzes the [_PackageDetails] according to a --mode and outputs a
   /// corresponding list of the versions
   /// [current, upgradable, resolvable, latest].
@@ -613,7 +613,7 @@
   Future<Pubspec> resolvablePubspec(Pubspec pubspec);
 }
 
-class _OutdatedMode implements Mode {
+class _OutdatedMode implements _Mode {
   @override
   String explanation(String directoryDescription) => '''
 Showing outdated packages$directoryDescription.
@@ -690,7 +690,7 @@
   }
 }
 
-class _NullSafetyMode implements Mode {
+class _NullSafetyMode implements _Mode {
   final SystemCache cache;
   final Entrypoint entrypoint;
   final bool shouldShowSpinner;
diff --git a/lib/src/command/remove.dart b/lib/src/command/remove.dart
index f3b27bd..82a9547 100644
--- a/lib/src/command/remove.dart
+++ b/lib/src/command/remove.dart
@@ -50,6 +50,9 @@
 
     argParser.addOption('directory',
         abbr: 'C', help: 'Run this in the directory<dir>.', valueHelp: 'dir');
+
+    argParser.addFlag('legacy-packages-file',
+        help: 'Generate the legacy ".packages" file', negatable: false);
   }
 
   @override
@@ -69,7 +72,8 @@
           .acquireDependencies(SolveType.get,
               precompile: argResults['precompile'],
               dryRun: true,
-              analytics: null);
+              analytics: null,
+              generateDotPackages: false);
     } else {
       /// Update the pubspec.
       _writeRemovalToPubspec(packages);
@@ -81,6 +85,7 @@
         SolveType.get,
         precompile: argResults['precompile'],
         analytics: analytics,
+        generateDotPackages: argResults['legacy-packages-file'],
       );
 
       var example = entrypoint.example;
@@ -90,6 +95,7 @@
           precompile: argResults['precompile'],
           onlyReportSuccessOrFailure: true,
           analytics: analytics,
+          generateDotPackages: argResults['legacy-packages-file'],
         );
       }
     }
diff --git a/lib/src/command/upgrade.dart b/lib/src/command/upgrade.dart
index 927d849..d6043c3 100644
--- a/lib/src/command/upgrade.dart
+++ b/lib/src/command/upgrade.dart
@@ -56,6 +56,9 @@
 
     argParser.addFlag('packages-dir', hide: true);
 
+    argParser.addFlag('legacy-packages-file',
+        help: 'Generate the legacy ".packages" file', negatable: false);
+
     argParser.addFlag(
       'major-versions',
       help: 'Upgrades packages to their latest resolvable versions, '
@@ -80,6 +83,8 @@
 
   bool get _precompile => argResults['precompile'];
 
+  bool get _packagesFile => argResults['legacy-packages-file'];
+
   bool get _upgradeNullSafety =>
       argResults['nullsafety'] || argResults['null-safety'];
 
@@ -126,6 +131,7 @@
       dryRun: _dryRun,
       precompile: _precompile,
       onlyReportSuccessOrFailure: onlySummary,
+      generateDotPackages: _packagesFile,
       analytics: analytics,
     );
     _showOfflineWarning();
@@ -237,6 +243,7 @@
         dryRun: true,
         precompile: _precompile,
         analytics: null, // No analytics for dry-run
+        generateDotPackages: false,
       );
     } else {
       if (changes.isNotEmpty) {
@@ -249,6 +256,7 @@
         SolveType.get,
         precompile: _precompile,
         analytics: analytics,
+        generateDotPackages: argResults['legacy-packages-file'],
       );
     }
 
@@ -333,6 +341,7 @@
         dryRun: true,
         precompile: _precompile,
         analytics: null,
+        generateDotPackages: false,
       );
     } else {
       if (changes.isNotEmpty) {
@@ -345,6 +354,7 @@
         SolveType.upgrade,
         precompile: _precompile,
         analytics: analytics,
+        generateDotPackages: argResults['legacy-packages-file'],
       );
     }
 
@@ -460,7 +470,7 @@
     final hasNoNullSafetyVersions = <String>{};
     final hasNullSafetyVersions = <String>{};
 
-    Future<Iterable<PackageRange>> _removeUpperConstraints(
+    Future<Iterable<PackageRange>> removeUpperConstraints(
       Iterable<PackageRange> dependencies,
     ) async =>
         await Future.wait(dependencies.map((dep) async {
@@ -490,8 +500,8 @@
           return dep.toRef().withConstraint(VersionConstraint.empty);
         }));
 
-    final deps = _removeUpperConstraints(original.dependencies.values);
-    final devDeps = _removeUpperConstraints(original.devDependencies.values);
+    final deps = removeUpperConstraints(original.dependencies.values);
+    final devDeps = removeUpperConstraints(original.devDependencies.values);
     await Future.wait([deps, devDeps]);
 
     if (hasNoNullSafetyVersions.isNotEmpty) {
diff --git a/lib/src/entrypoint.dart b/lib/src/entrypoint.dart
index b4a01a6..3a51b2f 100644
--- a/lib/src/entrypoint.dart
+++ b/lib/src/entrypoint.dart
@@ -51,7 +51,7 @@
   // sdks:
   //   dart: ">=1.2.3 <2.0.0"
   // ```
-  var sdkNames = sdks.keys.map((name) => '  ' + name).join('|');
+  var sdkNames = sdks.keys.map((name) => '  $name').join('|');
   return RegExp(r'^(' + sdkNames + r'|sdk): "?([^"]*)"?$', multiLine: true);
 }();
 
@@ -187,9 +187,10 @@
   /// Loads the entrypoint from a package at [rootDir].
   Entrypoint(
     String rootDir,
-    this.cache,
-  )   : root = Package.load(null, rootDir, cache.sources,
-            withPubspecOverrides: true),
+    this.cache, {
+    bool withPubspecOverrides = true,
+  })  : root = Package.load(null, rootDir, cache.sources,
+            withPubspecOverrides: withPubspecOverrides),
         globalDir = null;
 
   Entrypoint.inMemory(this.root, this.cache,
@@ -224,13 +225,17 @@
   Entrypoint? _example;
 
   /// Writes .packages and .dart_tool/package_config.json
-  Future<void> writePackagesFiles() async {
+  Future<void> writePackagesFiles({bool generateDotPackages = false}) async {
     final entrypointName = isGlobal ? null : root.name;
-    writeTextFile(
-        packagesFile,
-        lockFile.packagesFile(cache,
-            entrypoint: entrypointName,
-            relativeFrom: isGlobal ? null : root.dir));
+    if (generateDotPackages) {
+      writeTextFile(
+          packagesFile,
+          lockFile.packagesFile(cache,
+              entrypoint: entrypointName,
+              relativeFrom: isGlobal ? null : root.dir));
+    } else {
+      tryDeleteEntry(packagesFile);
+    }
     ensureDir(p.dirname(packageConfigFile));
     writeTextFile(
         packageConfigFile,
@@ -268,6 +273,7 @@
     Iterable<String>? unlock,
     bool dryRun = false,
     bool precompile = false,
+    required bool generateDotPackages,
     required PubAnalytics? analytics,
     bool onlyReportSuccessOrFailure = false,
   }) async {
@@ -341,7 +347,7 @@
       /// have to reload and reparse all the pubspecs.
       _packageGraph = PackageGraph.fromSolveResult(this, result);
 
-      await writePackagesFiles();
+      await writePackagesFiles(generateDotPackages: generateDotPackages);
 
       try {
         if (precompile) {
diff --git a/lib/src/executable.dart b/lib/src/executable.dart
index 5d8ea8b..3f8f6a9 100644
--- a/lib/src/executable.dart
+++ b/lib/src/executable.dart
@@ -308,6 +308,7 @@
         () => entrypoint.acquireDependencies(
           SolveType.get,
           analytics: analytics,
+          generateDotPackages: false,
         ),
       );
     } on ApplicationException catch (e) {
diff --git a/lib/src/global_packages.dart b/lib/src/global_packages.dart
index 82b1263..8bfe7cf 100644
--- a/lib/src/global_packages.dart
+++ b/lib/src/global_packages.dart
@@ -159,7 +159,11 @@
     var entrypoint = Entrypoint(path, cache);
 
     // Get the package's dependencies.
-    await entrypoint.acquireDependencies(SolveType.get, analytics: analytics);
+    await entrypoint.acquireDependencies(
+      SolveType.get,
+      analytics: analytics,
+      generateDotPackages: false,
+    );
     var name = entrypoint.root.name;
     _describeActive(name, cache);
 
@@ -773,12 +777,12 @@
         invocation = '''
 if [ -f $snapshot ]; then
   dart "$snapshot" "\$@"
-  # The VM exits with code 253 if the snapshot version is out-of-date.	
-  # If it is, we need to delete it and run "pub global" manually.	
-  exit_code=\$?	
-  if [ \$exit_code != 253 ]; then	
-    exit \$exit_code	
-  fi	
+  # The VM exits with code 253 if the snapshot version is out-of-date.
+  # If it is, we need to delete it and run "pub global" manually.
+  exit_code=\$?
+  if [ \$exit_code != 253 ]; then
+    exit \$exit_code
+  fi
   dart pub global run ${package.name}:$script "\$@"
 else
   dart pub global run ${package.name}:$script "\$@"
@@ -854,7 +858,7 @@
     if (Platform.isWindows) {
       // See if the shell can find one of the binstubs.
       // "\q" means return exit code 0 if found or 1 if not.
-      var result = runProcessSync('where', [r'\q', installed + '.bat']);
+      var result = runProcessSync('where', [r'\q', '$installed.bat']);
       if (result.exitCode == 0) return;
 
       log.warning("${log.yellow('Warning:')} Pub installs executables into "
diff --git a/lib/src/io.dart b/lib/src/io.dart
index 2faf85f..50f7eb9 100644
--- a/lib/src/io.dart
+++ b/lib/src/io.dart
@@ -390,7 +390,8 @@
     return null;
   }
 
-  for (var i = 0; i < 3; i++) {
+  const maxRetries = 50;
+  for (var i = 0; i < maxRetries; i++) {
     try {
       operation();
       break;
@@ -398,7 +399,7 @@
       var reason = getErrorReason(error);
       if (reason == null) rethrow;
 
-      if (i < 2) {
+      if (i < maxRetries - 1) {
         log.io('Pub failed to $description because $reason. '
             'Retrying in 50ms.');
         sleep(Duration(milliseconds: 50));
@@ -493,9 +494,9 @@
       // #define	ENOTEMPTY	39	/* Directory not empty */
       // https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/asm-generic/errno.h#n20
       (Platform.isLinux && errorCode == 39) ||
-          // On Windows this may fail with ERROR_DIR_NOT_EMPTY
+          // On Windows this may fail with ERROR_DIR_NOT_EMPTY or ERROR_ALREADY_EXISTS
           // https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-
-          (Platform.isWindows && errorCode == 145) ||
+          (Platform.isWindows && (errorCode == 145 || errorCode == 183)) ||
           // On MacOS rename will fail with ENOTEMPTY if directory exists.
           // #define ENOTEMPTY       66              /* Directory not empty */
           // https://github.com/apple-oss-distributions/xnu/blob/bb611c8fecc755a0d8e56e2fa51513527c5b7a0e/bsd/sys/errno.h#L190
@@ -702,7 +703,7 @@
 /// [environment] is provided, that will be used to augment (not replace) the
 /// the inherited variables.
 @visibleForTesting
-Future<_PubProcess> startProcess(
+Future<PubProcess> startProcess(
   String executable,
   List<String> args, {
   String? workingDir,
@@ -721,7 +722,7 @@
           'Pub failed to run subprocess `$executable`: $e');
     }
 
-    var process = _PubProcess(ioProcess);
+    var process = PubProcess(ioProcess);
     unawaited(process.exitCode.whenComplete(resource.release));
     return process;
   });
@@ -752,7 +753,7 @@
 }
 
 /// A wrapper around [Process] that exposes `dart:async`-style APIs.
-class _PubProcess {
+class PubProcess {
   /// The underlying `dart:io` [Process].
   final Process _process;
 
@@ -811,8 +812,8 @@
   /// error handler unless nothing has handled it.
   Future<int> get exitCode => _exitCode;
 
-  /// Creates a new [_PubProcess] wrapping [process].
-  _PubProcess(Process process) : _process = process {
+  /// Creates a new [PubProcess] wrapping [process].
+  PubProcess(Process process) : _process = process {
     var errorGroup = ErrorGroup();
 
     var pair = _consumerToSink(process.stdin);
diff --git a/lib/src/lock_file.dart b/lib/src/lock_file.dart
index 0950de1..215ab92 100644
--- a/lib/src/lock_file.dart
+++ b/lib/src/lock_file.dart
@@ -224,7 +224,7 @@
     String? relativeFrom,
   }) {
     var header = '''
-This file is deprecated. Tools should instead consume 
+This file is deprecated. Tools should instead consume
 `.dart_tool/package_config.json`.
 
 For more info see: https://dart.dev/go/dot-packages-deprecation
@@ -305,7 +305,7 @@
       generatorVersion: sdk.version,
     );
 
-    return JsonEncoder.withIndent('  ').convert(packageConfig.toJson()) + '\n';
+    return '${JsonEncoder.withIndent('  ').convert(packageConfig.toJson())}\n';
   }
 
   /// Returns the serialized YAML text of the lock file.
diff --git a/lib/src/package_config.dart b/lib/src/package_config.dart
index 0493dcc..e5e5e7d 100644
--- a/lib/src/package_config.dart
+++ b/lib/src/package_config.dart
@@ -2,6 +2,8 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+import 'dart:convert';
+
 import 'package:pub_semver/pub_semver.dart';
 
 import 'language_version.dart';
@@ -59,21 +61,23 @@
     }
     final root = data;
 
-    void _throw(String property, String mustBe) => throw FormatException(
-        '"$property" in .dart_tool/package_config.json $mustBe');
+    void throwFormatException(String property, String mustBe) =>
+        throw FormatException(
+            '"$property" in .dart_tool/package_config.json $mustBe');
 
     /// Read the 'configVersion' property
     final configVersion = root['configVersion'];
     if (configVersion is! int) {
-      _throw('configVersion', 'must be an integer');
+      throwFormatException('configVersion', 'must be an integer');
     }
     if (configVersion != 2) {
-      _throw('configVersion', 'must be 2 (the only supported version)');
+      throwFormatException(
+          'configVersion', 'must be 2 (the only supported version)');
     }
 
     final packagesRaw = root['packages'];
     if (packagesRaw is! List) {
-      _throw('packages', 'must be a list');
+      throwFormatException('packages', 'must be a list');
     }
     final packages = <PackageConfigEntry>[];
     for (final entry in packagesRaw) {
@@ -85,7 +89,7 @@
     final generatedRaw = root['generated'];
     if (generatedRaw != null) {
       if (generatedRaw is! String) {
-        _throw('generated', 'must be a string, if given');
+        throwFormatException('generated', 'must be a string, if given');
       }
       generated = DateTime.parse(generatedRaw);
     }
@@ -102,12 +106,12 @@
     final generatorVersionRaw = root['generatorVersion'];
     if (generatorVersionRaw != null) {
       if (generatorVersionRaw is! String) {
-        _throw('generatorVersion', 'must be a string, if given');
+        throwFormatException('generatorVersion', 'must be a string, if given');
       }
       try {
         generatorVersion = Version.parse(generatorVersionRaw);
       } on FormatException catch (e) {
-        _throw('generatorVersion',
+        throwFormatException('generatorVersion',
             'must be a semver version, if given, error: ${e.message}');
       }
     }
@@ -160,8 +164,6 @@
   /// Given as `<major>.<minor>` version, similar to the `// @dart = X.Y`
   /// comment. This is derived from the lower-bound on the Dart SDK requirement
   /// in the `pubspec.yaml` for the given package.
-  ///
-  /// `null` if not given.
   LanguageVersion? languageVersion;
 
   /// Additional properties not in the specification for the
@@ -173,10 +175,8 @@
     required this.rootUri,
     this.packageUri,
     this.languageVersion,
-    this.additionalProperties,
-  }) {
-    additionalProperties ??= {};
-  }
+    this.additionalProperties = const {},
+  });
 
   /// Create [PackageConfigEntry] from JSON [data].
   ///
@@ -189,30 +189,31 @@
     }
     final root = data;
 
-    Never _throw(String property, String mustBe) => throw FormatException(
-        '"packages[].$property" in .dart_tool/package_config.json $mustBe');
+    Never throwFormatException(String property, String mustBe) =>
+        throw FormatException(
+            '"packages[].$property" in .dart_tool/package_config.json $mustBe');
 
     final name = root['name'];
     if (name is! String) {
-      _throw('name', 'must be a string');
+      throwFormatException('name', 'must be a string');
     }
 
     final Uri rootUri;
     final rootUriRaw = root['rootUri'];
     if (rootUriRaw is! String) {
-      _throw('rootUri', 'must be a string');
+      throwFormatException('rootUri', 'must be a string');
     }
     try {
       rootUri = Uri.parse(rootUriRaw);
     } on FormatException {
-      _throw('rootUri', 'must be a URI');
+      throwFormatException('rootUri', 'must be a URI');
     }
 
     Uri? packageUri;
     var packageUriRaw = root['packageUri'];
     if (packageUriRaw != null) {
       if (packageUriRaw is! String) {
-        _throw('packageUri', 'must be a string');
+        throwFormatException('packageUri', 'must be a string');
       }
       if (!packageUriRaw.endsWith('/')) {
         packageUriRaw = '$packageUriRaw/';
@@ -220,7 +221,7 @@
       try {
         packageUri = Uri.parse(packageUriRaw);
       } on FormatException {
-        _throw('packageUri', 'must be a URI');
+        throwFormatException('packageUri', 'must be a URI');
       }
     }
 
@@ -228,12 +229,13 @@
     final languageVersionRaw = root['languageVersion'];
     if (languageVersionRaw != null) {
       if (languageVersionRaw is! String) {
-        _throw('languageVersion', 'must be a string');
+        throwFormatException('languageVersion', 'must be a string');
       }
       try {
         languageVersion = LanguageVersion.parse(languageVersionRaw);
       } on FormatException {
-        _throw('languageVersion', 'must be on the form <major>.<minor>');
+        throwFormatException(
+            'languageVersion', 'must be on the form <major>.<minor>');
       }
     }
 
@@ -249,7 +251,13 @@
   Map<String, Object?> toJson() => {
         'name': name,
         'rootUri': rootUri.toString(),
-        if (packageUri != null) 'packageUri': packageUri?.toString(),
+        if (packageUri != null) 'packageUri': packageUri.toString(),
         if (languageVersion != null) 'languageVersion': '$languageVersion',
       }..addAll(additionalProperties ?? {});
+
+  @override
+  String toString() {
+    // TODO: implement toString
+    return JsonEncoder.withIndent('  ').convert(toJson());
+  }
 }
diff --git a/lib/src/packages_file.dart b/lib/src/packages_file.dart
index 6833ae8..d5c7989 100644
--- a/lib/src/packages_file.dart
+++ b/lib/src/packages_file.dart
@@ -80,7 +80,7 @@
       packageLocation = baseLocation.resolve(packageValue);
       if (!packageLocation.path.endsWith('/')) {
         packageLocation =
-            packageLocation.replace(path: packageLocation.path + "/");
+            packageLocation.replace(path: "${packageLocation.path}/");
       }
     }
     if (result.containsKey(packageName)) {
@@ -162,7 +162,7 @@
       uri = _relativize(uri, baseUri);
     }
     if (!uri.path.endsWith('/')) {
-      uri = uri.replace(path: uri.path + '/');
+      uri = uri.replace(path: '${uri.path}/');
     }
     output.write(uri);
     output.writeln();
@@ -251,7 +251,7 @@
     }
     assert(badIndex < packageName.length);
     var badCharCode = packageName.codeUnitAt(badIndex);
-    var badChar = "U+" + badCharCode.toRadixString(16).padLeft(4, '0');
+    var badChar = "U+${badCharCode.toRadixString(16).padLeft(4, '0')}";
     if (badCharCode >= 0x20 && badCharCode <= 0x7e) {
       // Printable character.
       badChar = "'${packageName[badIndex]}' ($badChar)";
diff --git a/lib/src/pubspec.dart b/lib/src/pubspec.dart
index e18fbc6..d06b025 100644
--- a/lib/src/pubspec.dart
+++ b/lib/src/pubspec.dart
@@ -86,8 +86,7 @@
   /// The registry of sources to use when parsing [dependencies] and
   /// [devDependencies].
   ///
-  /// This will be null if this was created using [new Pubspec] or [new
-  /// Pubspec.empty].
+  /// This will be null if this was created using [Pubspec] or [Pubspec.empty].
   final SourceRegistry _sources;
 
   /// The location from which the pubspec was loaded.
@@ -431,7 +430,7 @@
   /// This will return at most one error for each field.
   List<PubspecException> get allErrors {
     var errors = <PubspecException>[];
-    void _collectError(void Function() fn) {
+    void collectError(void Function() fn) {
       try {
         fn();
       } on PubspecException catch (e) {
@@ -439,14 +438,14 @@
       }
     }
 
-    _collectError(() => name);
-    _collectError(() => version);
-    _collectError(() => dependencies);
-    _collectError(() => devDependencies);
-    _collectError(() => publishTo);
-    _collectError(() => executables);
-    _collectError(() => falseSecrets);
-    _collectError(_ensureEnvironment);
+    collectError(() => name);
+    collectError(() => version);
+    collectError(() => dependencies);
+    collectError(() => devDependencies);
+    collectError(() => publishTo);
+    collectError(() => executables);
+    collectError(() => falseSecrets);
+    collectError(_ensureEnvironment);
     return errors;
   }
 }
diff --git a/lib/src/pubspec_parse.dart b/lib/src/pubspec_parse.dart
index 68f9ce7..62eaa13 100644
--- a/lib/src/pubspec_parse.dart
+++ b/lib/src/pubspec_parse.dart
@@ -136,7 +136,7 @@
       final falseSecrets = <String>[];
 
       // Throws a [PubspecException]
-      void _falseSecretsError(SourceSpan span) => _error(
+      void falseSecretsError(SourceSpan span) => _error(
             '"false_secrets" field must be a list of git-ignore style patterns',
             span,
           );
@@ -147,12 +147,12 @@
           for (final node in falseSecretsNode.nodes) {
             final value = node.value;
             if (value is! String) {
-              _falseSecretsError(node.span);
+              falseSecretsError(node.span);
             }
             falseSecrets.add(value);
           }
         } else {
-          _falseSecretsError(falseSecretsNode.span);
+          falseSecretsError(falseSecretsNode.span);
         }
       }
 
diff --git a/lib/src/pubspec_utils.dart b/lib/src/pubspec_utils.dart
index cd28e21..71e30c4 100644
--- a/lib/src/pubspec_utils.dart
+++ b/lib/src/pubspec_utils.dart
@@ -108,7 +108,7 @@
   ArgumentError.checkNotNull(original, 'original');
   stripOnly ??= [];
 
-  List<PackageRange> _stripUpperBounds(
+  List<PackageRange> stripUpperBounds(
     Map<String, PackageRange> constrained,
   ) {
     final result = <PackageRange>[];
@@ -133,8 +133,8 @@
     original.name,
     version: original.version,
     sdkConstraints: original.sdkConstraints,
-    dependencies: _stripUpperBounds(original.dependencies),
-    devDependencies: _stripUpperBounds(original.devDependencies),
+    dependencies: stripUpperBounds(original.dependencies),
+    devDependencies: stripUpperBounds(original.devDependencies),
     dependencyOverrides: original.dependencyOverrides.values,
   );
 }
diff --git a/lib/src/rate_limited_scheduler.dart b/lib/src/rate_limited_scheduler.dart
index 85d73cd..078acbb 100644
--- a/lib/src/rate_limited_scheduler.dart
+++ b/lib/src/rate_limited_scheduler.dart
@@ -110,7 +110,7 @@
       return await callback((jobId) {
         if (_started.contains(jobId)) return;
         final task = _Task(jobId, Zone.current);
-        _cache.putIfAbsent(jobId, () => Completer());
+        _cache.putIfAbsent(jobId, Completer.new);
         _queue.addLast(task);
         prescheduled.add(task);
 
@@ -127,7 +127,7 @@
   /// If [jobId] is not yet running, it will go to the front of the work queue
   /// to be scheduled next when there are free resources.
   Future<V> schedule(J jobId) {
-    final completer = _cache.putIfAbsent(jobId, () => Completer());
+    final completer = _cache.putIfAbsent(jobId, Completer.new);
     if (!_started.contains(jobId)) {
       final task = _Task(jobId, Zone.current);
       _queue.addFirst(task);
diff --git a/lib/src/solver/failure.dart b/lib/src/solver/failure.dart
index 23b2e2f..5af4015 100644
--- a/lib/src/solver/failure.dart
+++ b/lib/src/solver/failure.dart
@@ -201,12 +201,8 @@
       var conflictLine = _lineNumbers[conflictClause.conflict];
       var otherLine = _lineNumbers[conflictClause.other];
       if (conflictLine != null && otherLine != null) {
-        _write(
-            incompatibility,
-            'Because ' +
-                conflictClause.conflict.andToString(conflictClause.other,
-                    detailsForCause, conflictLine, otherLine) +
-                ', $incompatibilityString.',
+        _write(incompatibility,
+            'Because ${conflictClause.conflict.andToString(conflictClause.other, detailsForCause, conflictLine, otherLine)}, $incompatibilityString.',
             numbered: numbered);
       } else if (conflictLine != null || otherLine != null) {
         Incompatibility withLine;
@@ -264,11 +260,8 @@
 
       var derivedLine = _lineNumbers[derived];
       if (derivedLine != null) {
-        _write(
-            incompatibility,
-            'Because ' +
-                ext.andToString(derived, detailsForCause, null, derivedLine) +
-                ', $incompatibilityString.',
+        _write(incompatibility,
+            'Because ${ext.andToString(derived, detailsForCause, null, derivedLine)}, $incompatibilityString.',
             numbered: numbered);
       } else if (_isCollapsible(derived)) {
         var derivedCause = derived.cause as ConflictCause;
diff --git a/lib/src/solver/incompatibility.dart b/lib/src/solver/incompatibility.dart
index 9e39865..04c7c3f 100644
--- a/lib/src/solver/incompatibility.dart
+++ b/lib/src/solver/incompatibility.dart
@@ -126,7 +126,7 @@
       if (!cause.sdk.isAvailable) {
         buffer.write('the ${cause.sdk.name} SDK');
       } else {
-        if (cause.sdk.name != 'Dart') buffer.write(cause.sdk.name + ' ');
+        if (cause.sdk.name != 'Dart') buffer.write('${cause.sdk.name} ');
         buffer.write('SDK version ${cause.constraint}');
       }
       return buffer.toString();
@@ -262,7 +262,7 @@
         .join(' or ');
 
     var buffer =
-        StringBuffer(_terse(thisPositive, details, allowEvery: true) + ' ');
+        StringBuffer('${_terse(thisPositive, details, allowEvery: true)} ');
     var isDependency = cause == IncompatibilityCause.dependency &&
         other.cause == IncompatibilityCause.dependency;
     buffer.write(isDependency ? 'depends on' : 'requires');
@@ -414,7 +414,7 @@
       if (!cause.sdk.isAvailable) {
         buffer.write('the ${cause.sdk.name} SDK');
       } else {
-        if (cause.sdk.name != 'Dart') buffer.write(cause.sdk.name + ' ');
+        if (cause.sdk.name != 'Dart') buffer.write('${cause.sdk.name} ');
         buffer.write('SDK version ${cause.constraint}');
       }
     } else if (latter.cause == IncompatibilityCause.noVersions) {
diff --git a/lib/src/source/hosted.dart b/lib/src/source/hosted.dart
index f403c60..e7dc870 100644
--- a/lib/src/source/hosted.dart
+++ b/lib/src/source/hosted.dart
@@ -77,7 +77,7 @@
   }
   // If there is a path, and it doesn't end in a slash we normalize to slash
   if (u.path.isNotEmpty && !u.path.endsWith('/')) {
-    u = u.replace(path: u.path + '/');
+    u = u.replace(path: '${u.path}/');
   }
   // pub.dev and pub.dartlang.org are identical.
   //
diff --git a/lib/src/source/sdk.dart b/lib/src/source/sdk.dart
index fe10eb4..da492f5 100644
--- a/lib/src/source/sdk.dart
+++ b/lib/src/source/sdk.dart
@@ -118,7 +118,7 @@
       // [PackageNotFoundException]s are uncapitalized and unpunctuated because
       // they're used within other sentences by the version solver, but
       // [ApplicationException]s should be full sentences.
-      throw ApplicationException(capitalize(error.message) + '.');
+      throw ApplicationException('${capitalize(error.message)}.');
     }
   }
 }
diff --git a/lib/src/third_party/tar/README.md b/lib/src/third_party/tar/README.md
index a2a220f..a2fbf7b 100644
--- a/lib/src/third_party/tar/README.md
+++ b/lib/src/third_party/tar/README.md
@@ -4,4 +4,4 @@
 tar-archives.
 
  * Repository: `https://github.com/simolus3/tar/`
- * Revision: `7cdb563c9894600c6a739ec268f8673d6122006f`
+ * Revision: `901ae404e0a225d9b08e5253415ca092f5c08706`
diff --git a/lib/src/third_party/tar/src/charcodes.dart b/lib/src/third_party/tar/src/charcodes.dart
index 1d34a6d..de0762f 100644
--- a/lib/src/third_party/tar/src/charcodes.dart
+++ b/lib/src/third_party/tar/src/charcodes.dart
@@ -1,3 +1,6 @@
+@internal
+import 'package:meta/meta.dart';
+
 /// "Line feed" control character.
 const int $lf = 0x0a;
 
diff --git a/lib/src/third_party/tar/src/constants.dart b/lib/src/third_party/tar/src/constants.dart
index 05accb0..d74a2b0 100644
--- a/lib/src/third_party/tar/src/constants.dart
+++ b/lib/src/third_party/tar/src/constants.dart
@@ -1,8 +1,11 @@
+@internal
 import 'dart:typed_data';
 
+import 'package:meta/meta.dart';
+
 import 'charcodes.dart';
 import 'exception.dart';
-import 'header.dart' show TarHeader; // for dartdoc
+import 'header.dart';
 
 // Magic values to help us identify the TAR header type.
 const magicGnu = [$u, $s, $t, $a, $r, $space]; // 'ustar '
@@ -11,74 +14,6 @@
 const versionUstar = [$0, $0]; // '00'
 const trailerStar = [$t, $a, $r, 0]; // 'tar\x00'
 
-/// Type flags for [TarHeader].
-///
-/// The type flag of a header indicates the kind of file associated with the
-/// entry. This enum contains the various type flags over the different TAR
-/// formats, and users should be careful that the type flag corresponds to the
-/// TAR format they are working with.
-enum TypeFlag {
-  /// [reg] indicates regular files.
-  ///
-  /// Old tar implementations have a seperate `TypeRegA` value. This library
-  /// will transparently read those as [regA].
-  reg,
-
-  /// Legacy-version of [reg] in old tar implementations.
-  ///
-  /// This is only used internally.
-  regA,
-
-  /// Hard link - header-only, may not have a data body
-  link,
-
-  /// Symbolic link - header-only, may not have a data body
-  symlink,
-
-  /// Character device node - header-only, may not have a data body
-  char,
-
-  /// Block device node - header-only, may not have a data body
-  block,
-
-  /// Directory - header-only, may not have a data body
-  dir,
-
-  /// FIFO node - header-only, may not have a data body
-  fifo,
-
-  /// Currently does not have any meaning, but is reserved for the future.
-  reserved,
-
-  /// Used by the PAX format to store key-value records that are only relevant
-  /// to the next file.
-  ///
-  /// This package transparently handles these types.
-  xHeader,
-
-  /// Used by the PAX format to store key-value records that are relevant to all
-  /// subsequent files.
-  ///
-  /// This package only supports parsing and composing such headers,
-  /// but does not currently support persisting the global state across files.
-  xGlobalHeader,
-
-  /// Indiates a sparse file in the GNU format
-  gnuSparse,
-
-  /// Used by the GNU format for a meta file to store the path or link name for
-  /// the next file.
-  /// This package transparently handles these types.
-  gnuLongName,
-  gnuLongLink,
-
-  /// Vendor specific typeflag, as defined in POSIX.1-1998. Seen as outdated but
-  /// may still exist on old files.
-  ///
-  /// This library uses a single enum to catch them all.
-  vendor
-}
-
 /// Generates the corresponding [TypeFlag] associated with [byte].
 TypeFlag typeflagFromByte(int byte) {
   switch (byte) {
diff --git a/lib/src/third_party/tar/src/entry.dart b/lib/src/third_party/tar/src/entry.dart
index 160974b..71d1730 100644
--- a/lib/src/third_party/tar/src/entry.dart
+++ b/lib/src/third_party/tar/src/entry.dart
@@ -2,7 +2,6 @@
 
 import 'package:meta/meta.dart';
 
-import 'constants.dart';
 import 'header.dart';
 
 /// An entry in a tar file.
diff --git a/lib/src/third_party/tar/src/format.dart b/lib/src/third_party/tar/src/format.dart
index b6be2f5..ef65ec9 100644
--- a/lib/src/third_party/tar/src/format.dart
+++ b/lib/src/third_party/tar/src/format.dart
@@ -28,7 +28,7 @@
   int get hashCode => _value;
 
   @override
-  bool operator ==(Object? other) {
+  bool operator ==(Object other) {
     if (other is! TarFormat) return false;
 
     return _value == other._value;
diff --git a/lib/src/third_party/tar/src/header.dart b/lib/src/third_party/tar/src/header.dart
index f28dc79..2f35662 100644
--- a/lib/src/third_party/tar/src/header.dart
+++ b/lib/src/third_party/tar/src/header.dart
@@ -7,6 +7,74 @@
 import 'format.dart';
 import 'utils.dart';
 
+/// Type flags for [TarHeader].
+///
+/// The type flag of a header indicates the kind of file associated with the
+/// entry. This enum contains the various type flags over the different TAR
+/// formats, and users should be careful that the type flag corresponds to the
+/// TAR format they are working with.
+enum TypeFlag {
+  /// [reg] indicates regular files.
+  ///
+  /// Old tar implementations have a seperate `TypeRegA` value. This library
+  /// will transparently read those as [regA].
+  reg,
+
+  /// Legacy-version of [reg] in old tar implementations.
+  ///
+  /// This is only used internally.
+  regA,
+
+  /// Hard link - header-only, may not have a data body
+  link,
+
+  /// Symbolic link - header-only, may not have a data body
+  symlink,
+
+  /// Character device node - header-only, may not have a data body
+  char,
+
+  /// Block device node - header-only, may not have a data body
+  block,
+
+  /// Directory - header-only, may not have a data body
+  dir,
+
+  /// FIFO node - header-only, may not have a data body
+  fifo,
+
+  /// Currently does not have any meaning, but is reserved for the future.
+  reserved,
+
+  /// Used by the PAX format to store key-value records that are only relevant
+  /// to the next file.
+  ///
+  /// This package transparently handles these types.
+  xHeader,
+
+  /// Used by the PAX format to store key-value records that are relevant to all
+  /// subsequent files.
+  ///
+  /// This package only supports parsing and composing such headers,
+  /// but does not currently support persisting the global state across files.
+  xGlobalHeader,
+
+  /// Indiates a sparse file in the GNU format
+  gnuSparse,
+
+  /// Used by the GNU format for a meta file to store the path or link name for
+  /// the next file.
+  /// This package transparently handles these types.
+  gnuLongName,
+  gnuLongLink,
+
+  /// Vendor specific typeflag, as defined in POSIX.1-1998. Seen as outdated but
+  /// may still exist on old files.
+  ///
+  /// This library uses a single enum to catch them all.
+  vendor
+}
+
 /// Header of a tar entry
 ///
 /// A tar header stores meta-information about the matching tar entry, such as
diff --git a/lib/src/third_party/tar/src/sparse.dart b/lib/src/third_party/tar/src/sparse.dart
index 06b88b1..35c0311 100644
--- a/lib/src/third_party/tar/src/sparse.dart
+++ b/lib/src/third_party/tar/src/sparse.dart
@@ -1,3 +1,4 @@
+@internal
 import 'package:async/async.dart';
 import 'package:meta/meta.dart';
 
@@ -21,7 +22,7 @@
   String toString() => 'offset: $offset, length $length';
 
   @override
-  bool operator ==(Object? other) {
+  bool operator ==(Object other) {
     if (other is! SparseEntry) return false;
 
     return offset == other.offset && length == other.length;
diff --git a/lib/src/third_party/tar/src/utils.dart b/lib/src/third_party/tar/src/utils.dart
index 4fa75b1..e2bb5b6 100644
--- a/lib/src/third_party/tar/src/utils.dart
+++ b/lib/src/third_party/tar/src/utils.dart
@@ -1,8 +1,11 @@
+@internal
 import 'dart:async';
 import 'dart:convert';
 import 'dart:math';
 import 'dart:typed_data';
 
+import 'package:meta/meta.dart';
+
 import 'charcodes.dart';
 import 'constants.dart';
 import 'exception.dart';
@@ -95,7 +98,7 @@
   int computeUnsignedHeaderChecksum() {
     // Accessing the last element first helps the VM eliminate bounds checks in
     // the loops below.
-    this[blockSize - 1];
+    this[blockSize - 1]; // ignore: unnecessary_statements
     var result = checksumLength * _checksumPlaceholder;
 
     for (var i = 0; i < checksumOffset; i++) {
@@ -109,7 +112,7 @@
   }
 
   int computeSignedHeaderChecksum() {
-    this[blockSize - 1];
+    this[blockSize - 1]; // ignore: unnecessary_statements
     // Note that _checksumPlaceholder.toSigned(8) == _checksumPlaceholder
     var result = checksumLength * _checksumPlaceholder;
 
@@ -445,7 +448,7 @@
 
     var state = _StreamState.initial;
 
-    /// Sends trailing data to the stream. Reeturns true if the subscription
+    /// Sends trailing data to the stream. Returns true if the subscription
     /// should still be resumed afterwards.
     bool emitTrailing() {
       // Attempt to serve requests from pending data first.
@@ -512,13 +515,25 @@
     controller
       ..onListen = scheduleInitialEmit
       ..onPause = () {
-        assert(state == _StreamState.initial || state == _StreamState.attached);
+        assert(
+            state == _StreamState.initial ||
+                state == _StreamState.attached ||
+                state == _StreamState.done,
+            'Unexpected pause event in $state ($_remainingBlocksInOutgoing blocks remaining).');
 
         if (state == _StreamState.initial) {
           state = _StreamState.pausedAfterInitial;
-        } else {
+        } else if (state == _StreamState.attached) {
           _pause();
           state = _StreamState.pausedAfterAttached;
+        } else if (state == _StreamState.done) {
+          // It may happen that onPause is called in a state where we believe
+          // the stream to be done already. After the stream is done, we close
+          // the controller in a new microtask. So if the subscription is paused
+          // after the last event it emitted but before we close the controller,
+          // we can get a pause event here.
+          // There's nothing to do in that case.
+          assert(_subscription?.isPaused != false);
         }
       }
       ..onResume = () {
diff --git a/lib/src/third_party/tar/tar.dart b/lib/src/third_party/tar/tar.dart
index 14247bd..9948c4f 100644
--- a/lib/src/third_party/tar/tar.dart
+++ b/lib/src/third_party/tar/tar.dart
@@ -8,10 +8,9 @@
 import 'src/reader.dart';
 import 'src/writer.dart';
 
-export 'src/constants.dart' show TypeFlag;
 export 'src/entry.dart' show TarEntry, SynchronousTarEntry;
 export 'src/exception.dart';
 export 'src/format.dart';
-export 'src/header.dart' show TarHeader;
+export 'src/header.dart' show TarHeader, TypeFlag;
 export 'src/reader.dart' show TarReader;
 export 'src/writer.dart';
diff --git a/lib/src/utils.dart b/lib/src/utils.dart
index 2a5768e..3e1cf48 100644
--- a/lib/src/utils.dart
+++ b/lib/src/utils.dart
@@ -186,7 +186,7 @@
 /// commas and/or [conjunction] (`"and"` by default) where appropriate.
 String toSentence(Iterable iter, {String conjunction = 'and'}) {
   if (iter.length == 1) return iter.first.toString();
-  return iter.take(iter.length - 1).join(', ') + ' $conjunction ${iter.last}';
+  return '${iter.take(iter.length - 1).join(', ')} $conjunction ${iter.last}';
 }
 
 /// Returns [name] if [number] is 1, or the plural of [name] otherwise.
@@ -447,7 +447,7 @@
 String yamlToString(data) {
   var buffer = StringBuffer();
 
-  void _stringify(bool isMapValue, String indent, data) {
+  void stringify(bool isMapValue, String indent, data) {
     // TODO(nweiz): Serialize using the YAML library once it supports
     // serialization.
 
@@ -473,7 +473,7 @@
         }
 
         buffer.write('$indent$keyString:');
-        _stringify(true, indent, data[key]);
+        stringify(true, indent, data[key]);
       }
 
       return;
@@ -495,7 +495,7 @@
     }
   }
 
-  _stringify(false, '', data);
+  stringify(false, '', data);
   return buffer.toString();
 }
 
diff --git a/lib/src/validator/dependency.dart b/lib/src/validator/dependency.dart
index 40e9c2a..fa01b45 100644
--- a/lib/src/validator/dependency.dart
+++ b/lib/src/validator/dependency.dart
@@ -27,11 +27,11 @@
   @override
   Future validate() async {
     /// Whether any dependency has a caret constraint.
-    var _hasCaretDep = false;
+    var hasCaretDep = false;
 
     /// Emit an error for dependencies from unknown SDKs or without appropriate
     /// constraints on the Dart SDK.
-    void _warnAboutSdkSource(PackageRange dep) {
+    void warnAboutSdkSource(PackageRange dep) {
       var identifier = (dep.description as SdkDescription).sdk;
       var sdk = sdks[identifier];
       if (sdk == null) {
@@ -44,7 +44,7 @@
     }
 
     /// Warn that dependencies should use the hosted source.
-    Future _warnAboutSource(PackageRange dep) async {
+    Future warnAboutSource(PackageRange dep) async {
       List<Version> versions;
       try {
         var ids = await entrypoint.cache
@@ -78,9 +78,9 @@
     }
 
     /// Warn about improper dependencies on Flutter.
-    void _warnAboutFlutterSdk(PackageRange dep) {
+    void warnAboutFlutterSdk(PackageRange dep) {
       if (dep.source is SdkSource) {
-        _warnAboutSdkSource(dep);
+        warnAboutSdkSource(dep);
         return;
       }
 
@@ -95,7 +95,7 @@
     }
 
     /// Warn that dependencies should have version constraints.
-    void _warnAboutNoConstraint(PackageRange dep) {
+    void warnAboutNoConstraint(PackageRange dep) {
       var message = 'Your dependency on "${dep.name}" should have a version '
           'constraint.';
       var locked = entrypoint.lockFile.packages[dep.name];
@@ -111,7 +111,7 @@
     }
 
     /// Warn that dependencies should allow more than a single version.
-    void _warnAboutSingleVersionConstraint(PackageRange dep) {
+    void warnAboutSingleVersionConstraint(PackageRange dep) {
       warnings.add(
           'Your dependency on "${dep.name}" should allow more than one version. '
           'For example:\n'
@@ -125,7 +125,7 @@
     }
 
     /// Warn that dependencies should have lower bounds on their constraints.
-    void _warnAboutNoConstraintLowerBound(PackageRange dep) {
+    void warnAboutNoConstraintLowerBound(PackageRange dep) {
       var message =
           'Your dependency on "${dep.name}" should have a lower bound.';
       var locked = entrypoint.lockFile.packages[dep.name];
@@ -148,7 +148,7 @@
     }
 
     /// Warn that dependencies should have upper bounds on their constraints.
-    void _warnAboutNoConstraintUpperBound(PackageRange dep) {
+    void warnAboutNoConstraintUpperBound(PackageRange dep) {
       String constraint;
       if ((dep.constraint as VersionRange).includeMin) {
         constraint = '^${(dep.constraint as VersionRange).min}';
@@ -169,7 +169,7 @@
           '${log.bold("all")} future versions of ${dep.name}.');
     }
 
-    void _warnAboutPrerelease(String dependencyName, VersionRange constraint) {
+    void warnAboutPrerelease(String dependencyName, VersionRange constraint) {
       final packageVersion = entrypoint.root.version;
       if (constraint.min != null &&
           constraint.min!.isPreRelease &&
@@ -184,15 +184,15 @@
     }
 
     /// Validates all dependencies in [dependencies].
-    Future _validateDependencies(Iterable<PackageRange> dependencies) async {
+    Future validateDependencies(Iterable<PackageRange> dependencies) async {
       for (var dependency in dependencies) {
         var constraint = dependency.constraint;
         if (dependency.name == 'flutter') {
-          _warnAboutFlutterSdk(dependency);
+          warnAboutFlutterSdk(dependency);
         } else if (dependency.source is SdkSource) {
-          _warnAboutSdkSource(dependency);
+          warnAboutSdkSource(dependency);
         } else if (dependency.source is! HostedSource) {
-          await _warnAboutSource(dependency);
+          await warnAboutSource(dependency);
 
           final description = dependency.description;
           if (description is GitDescription && description.path != '.') {
@@ -201,28 +201,27 @@
           }
         } else {
           if (constraint.isAny) {
-            _warnAboutNoConstraint(dependency);
+            warnAboutNoConstraint(dependency);
           } else if (constraint is VersionRange) {
             if (constraint is Version) {
-              _warnAboutSingleVersionConstraint(dependency);
+              warnAboutSingleVersionConstraint(dependency);
             } else {
-              _warnAboutPrerelease(dependency.name, constraint);
+              warnAboutPrerelease(dependency.name, constraint);
               if (constraint.min == null) {
-                _warnAboutNoConstraintLowerBound(dependency);
+                warnAboutNoConstraintLowerBound(dependency);
               } else if (constraint.max == null) {
-                _warnAboutNoConstraintUpperBound(dependency);
+                warnAboutNoConstraintUpperBound(dependency);
               }
             }
-            _hasCaretDep =
-                _hasCaretDep || constraint.toString().startsWith('^');
+            hasCaretDep = hasCaretDep || constraint.toString().startsWith('^');
           }
         }
       }
     }
 
-    await _validateDependencies(entrypoint.root.pubspec.dependencies.values);
+    await validateDependencies(entrypoint.root.pubspec.dependencies.values);
 
-    if (_hasCaretDep) {
+    if (hasCaretDep) {
       validateSdkConstraint(_firstCaretVersion,
           "Older versions of pub don't support ^ version constraints.");
     }
diff --git a/lib/src/validator/null_safety_mixed_mode.dart b/lib/src/validator/null_safety_mixed_mode.dart
index 36f9796..31b755c 100644
--- a/lib/src/validator/null_safety_mixed_mode.dart
+++ b/lib/src/validator/null_safety_mixed_mode.dart
@@ -31,7 +31,6 @@
         ),
       ),
     );
-    print(analysisResult.compliance);
     if (analysisResult.compliance == NullSafetyCompliance.mixed) {
       warnings.add('''
 This package is opting into null-safety, but a dependency or file is not.
@@ -39,10 +38,10 @@
 ${analysisResult.reason}
 
 Note that by publishing with non-migrated dependencies your package may be
-broken at any time if one of your dependencies migrates without a breaking 
-change release. 
+broken at any time if one of your dependencies migrates without a breaking
+change release.
 
-We highly recommend that you wait until all of your dependencies have been 
+We highly recommend that you wait until all of your dependencies have been
 migrated before publishing.
 
 Run `pub outdated --mode=null-safety` for more information about the state of
diff --git a/pubspec.yaml b/pubspec.yaml
index f39e27a..e44e0e7 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,7 +1,7 @@
 name: pub
 
 environment:
-  sdk: '>=2.12.0 <3.0.0'
+  sdk: '>=2.17.0 <3.0.0'
 
 dependencies:
   # Note: Pub's test infrastructure assumes that any dependencies used in tests
@@ -15,6 +15,7 @@
   frontend_server_client: ^2.0.0
   http: ^0.13.3
   http_multi_server: ^3.0.1
+  http_parser: ^4.0.1
   meta: ^1.3.0
   oauth2: ^2.0.0
   path: ^1.8.0
@@ -28,7 +29,7 @@
   yaml_edit: ^2.0.0
 
 dev_dependencies:
-  lints: ^1.0.1
+  lints: ^2.0.0
   shelf_test_handler: ^2.0.0
   test: ^1.17.3
   test_descriptor: ^2.0.0
diff --git a/test/add/common/add_test.dart b/test/add/common/add_test.dart
index 1a1e9df..02459cc 100644
--- a/test/add/common/add_test.dart
+++ b/test/add/common/add_test.dart
@@ -7,6 +7,7 @@
 import 'package:path/path.dart' as p;
 import 'package:pub/src/exit_codes.dart' as exit_codes;
 import 'package:test/test.dart';
+import 'package:yaml/yaml.dart';
 
 import '../../descriptor.dart' as d;
 import '../../test_pub.dart';
@@ -46,7 +47,9 @@
       await pubAdd(args: ['foo:1.2.3']);
 
       await d.cacheDir({'foo': '1.2.3'}).validate();
-      await d.appPackagesFile({'foo': '1.2.3'}).validate();
+      await d.appPackageConfigFile([
+        d.packageConfigEntry(name: 'foo', version: '1.2.3'),
+      ]).validate();
       await d.appDir({'foo': '1.2.3'}).validate();
     });
 
@@ -62,8 +65,11 @@
 
       await d.cacheDir(
           {'foo': '1.2.3', 'bar': '1.1.0', 'baz': '2.5.3'}).validate();
-      await d.appPackagesFile(
-          {'foo': '1.2.3', 'bar': '1.1.0', 'baz': '2.5.3'}).validate();
+      await d.appPackageConfigFile([
+        d.packageConfigEntry(name: 'foo', version: '1.2.3'),
+        d.packageConfigEntry(name: 'bar', version: '1.1.0'),
+        d.packageConfigEntry(name: 'baz', version: '2.5.3'),
+      ]).validate();
       await d
           .appDir({'foo': '1.2.3', 'bar': '1.1.0', 'baz': '2.5.3'}).validate();
     });
@@ -90,7 +96,9 @@
       await pubAdd(args: ['foo:1.2.3']);
 
       await d.cacheDir({'foo': '1.2.3'}).validate();
-      await d.appPackagesFile({'foo': '1.2.3'}).validate();
+      await d.appPackageConfigFile([
+        d.packageConfigEntry(name: 'foo', version: '1.2.3'),
+      ]).validate();
 
       await d.dir(appPath, [
         d.pubspec({
@@ -130,13 +138,52 @@
       server.serve('foo', '1.2.3');
 
       await d.dir(appPath, [
-        d.pubspec({'name': 'myapp'})
+        d.file('pubspec.yaml', '''
+name: myapp
+environment:
+  "sdk": ">=0.1.2 <1.0.0"
+''')
+      ]).create();
+
+      await pubAdd(args: ['foo:1.2.3']);
+      print(
+          File(p.join(d.sandbox, appPath, 'pubspec.yaml')).readAsStringSync());
+      final yaml = loadYaml(
+          File(p.join(d.sandbox, appPath, 'pubspec.yaml')).readAsStringSync());
+
+      expect(((yaml as YamlMap).nodes['dependencies'] as YamlMap).style,
+          CollectionStyle.BLOCK,
+          reason: 'Should create the mapping with block-style by default');
+      await d.cacheDir({'foo': '1.2.3'}).validate();
+      await d.appPackageConfigFile([
+        d.packageConfigEntry(name: 'foo', version: '1.2.3'),
+      ]).validate();
+      await d.appDir({'foo': '1.2.3'}).validate();
+    });
+
+    test('Inserts correctly when the pubspec is flow-style at top-level',
+        () async {
+      final server = await servePackages();
+      server.serve('foo', '1.2.3');
+
+      await d.dir(appPath, [
+        d.file('pubspec.yaml',
+            '{"name":"myapp", "environment": {"sdk": ">=0.1.2 <1.0.0"}}')
       ]).create();
 
       await pubAdd(args: ['foo:1.2.3']);
 
+      final yaml = loadYaml(
+          File(p.join(d.sandbox, appPath, 'pubspec.yaml')).readAsStringSync());
+
+      expect(((yaml as YamlMap).nodes['dependencies'] as YamlMap).style,
+          CollectionStyle.FLOW,
+          reason: 'Should not break a pubspec in flow-style');
+
       await d.cacheDir({'foo': '1.2.3'}).validate();
-      await d.appPackagesFile({'foo': '1.2.3'}).validate();
+      await d.appPackageConfigFile([
+        d.packageConfigEntry(name: 'foo', version: '1.2.3'),
+      ]).validate();
       await d.appDir({'foo': '1.2.3'}).validate();
     });
 
@@ -217,7 +264,9 @@
               'adding it to dependencies instead.'));
 
       await d.cacheDir({'foo': '1.2.3'}).validate();
-      await d.appPackagesFile({'foo': '1.2.3'}).validate();
+      await d.appPackageConfigFile([
+        d.packageConfigEntry(name: 'foo', version: '1.2.3'),
+      ]).validate();
 
       await d.dir(appPath, [
         d.pubspec({
@@ -244,7 +293,9 @@
         await pubAdd(args: ['foo']);
 
         await d.cacheDir({'foo': '1.2.2'}).validate();
-        await d.appPackagesFile({'foo': '1.2.2'}).validate();
+        await d.appPackageConfigFile([
+          d.packageConfigEntry(name: 'foo', version: '1.2.2'),
+        ]).validate();
         await d.dir(appPath, [
           d.pubspec({
             'name': 'myapp',
@@ -456,7 +507,9 @@
 
       await pubAdd(args: ['--dev', 'foo:1.2.3']);
 
-      await d.appPackagesFile({'foo': '1.2.3'}).validate();
+      await d.appPackageConfigFile([
+        d.packageConfigEntry(name: 'foo', version: '1.2.3'),
+      ]).validate();
 
       await d.dir(appPath, [
         d.pubspec({
@@ -566,7 +619,9 @@
         await pubAdd(args: ['foo', '--dev']);
 
         await d.cacheDir({'foo': '1.2.2'}).validate();
-        await d.appPackagesFile({'foo': '1.2.2'}).validate();
+        await d.appPackageConfigFile([
+          d.packageConfigEntry(name: 'foo', version: '1.2.2'),
+        ]).validate();
         await d.dir(appPath, [
           d.pubspec({
             'name': 'myapp',
diff --git a/test/add/common/version_constraint_test.dart b/test/add/common/version_constraint_test.dart
index 546642f..f058082 100644
--- a/test/add/common/version_constraint_test.dart
+++ b/test/add/common/version_constraint_test.dart
@@ -22,7 +22,9 @@
     await pubAdd(args: ['foo']);
 
     await d.cacheDir({'foo': '1.2.3'}).validate();
-    await d.appPackagesFile({'foo': '1.2.3'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.2.3'),
+    ]).validate();
     await d.appDir({'foo': '^1.2.3'}).validate();
   });
 
@@ -35,7 +37,9 @@
     await pubAdd(args: ['foo:1.2.3']);
 
     await d.cacheDir({'foo': '1.2.3'}).validate();
-    await d.appPackagesFile({'foo': '1.2.3'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.2.3'),
+    ]).validate();
     await d.appDir({'foo': '1.2.3'}).validate();
   });
 
@@ -48,7 +52,9 @@
     await pubAdd(args: ['foo:1.2.3-dev']);
 
     await d.cacheDir({'foo': '1.2.3-dev'}).validate();
-    await d.appPackagesFile({'foo': '1.2.3-dev'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.2.3-dev'),
+    ]).validate();
     await d.appDir({'foo': '1.2.3-dev'}).validate();
   });
 
@@ -65,7 +71,9 @@
     await pubAdd(args: ['foo:any']);
 
     await d.cacheDir({'foo': '1.2.3'}).validate();
-    await d.appPackagesFile({'foo': '1.2.3'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.2.3'),
+    ]).validate();
     await d.appDir({'foo': 'any'}).validate();
   });
 
@@ -78,7 +86,9 @@
     await pubAdd(args: ['foo:>1.2.0 <2.0.0']);
 
     await d.cacheDir({'foo': '1.2.3'}).validate();
-    await d.appPackagesFile({'foo': '1.2.3'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.2.3'),
+    ]).validate();
     await d.appDir({'foo': '>1.2.0 <2.0.0'}).validate();
   });
 
@@ -98,7 +108,10 @@
     await d.appDir({'foo': '^0.1.0', 'bar': '2.0.3'}).validate();
 
     await d.cacheDir({'foo': '0.1.0', 'bar': '2.0.3'}).validate();
-    await d.appPackagesFile({'foo': '0.1.0', 'bar': '2.0.3'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '0.1.0'),
+      d.packageConfigEntry(name: 'bar', version: '2.0.3'),
+    ]).validate();
   });
 
   group('does not update pubspec if no available version found', () {
diff --git a/test/add/common/version_resolution_test.dart b/test/add/common/version_resolution_test.dart
index 64b393a..0f44d2b 100644
--- a/test/add/common/version_resolution_test.dart
+++ b/test/add/common/version_resolution_test.dart
@@ -32,7 +32,10 @@
 
     await d.appDir({'foo': '^3.5.0', 'bar': '1.0.0'}).validate();
     await d.cacheDir({'foo': '3.5.0', 'bar': '1.0.0'}).validate();
-    await d.appPackagesFile({'foo': '3.5.0', 'bar': '1.0.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '3.5.0'),
+      d.packageConfigEntry(name: 'bar', version: '1.0.0'),
+    ]).validate();
   });
 
   test('chooses the appropriate version to not break other dependencies',
@@ -54,7 +57,10 @@
 
     await d.appDir({'foo': '^3.2.1', 'bar': '1.0.0'}).validate();
     await d.cacheDir({'foo': '3.2.1', 'bar': '1.0.0'}).validate();
-    await d.appPackagesFile({'foo': '3.2.1', 'bar': '1.0.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '3.2.1'),
+      d.packageConfigEntry(name: 'bar', version: '1.0.0'),
+    ]).validate();
   });
 
   test('may upgrade other packages if they allow a later version to be chosen',
@@ -78,6 +84,9 @@
 
     await d.appDir({'foo': '^4.0.0', 'bar': '^1.0.0'}).validate();
     await d.cacheDir({'foo': '4.0.0', 'bar': '1.5.0'}).validate();
-    await d.appPackagesFile({'foo': '4.0.0', 'bar': '1.5.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '4.0.0'),
+      d.packageConfigEntry(name: 'bar', version: '1.5.0'),
+    ]).validate();
   });
 }
diff --git a/test/add/git/git_test.dart b/test/add/git/git_test.dart
index 00bd334..d976172 100644
--- a/test/add/git/git_test.dart
+++ b/test/add/git/git_test.dart
@@ -173,7 +173,9 @@
     await pubAdd(args: ['foo', '--git-url', '../foo.git']);
 
     await d.cacheDir({'foo': '1.2.2'}).validate();
-    await d.appPackagesFile({'foo': '1.2.2'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.2.2'),
+    ]).validate();
     await d.dir(appPath, [
       d.pubspec({
         'name': 'myapp',
diff --git a/test/add/git/subdir_test.dart b/test/add/git/subdir_test.dart
index 0f78069..8e8c39c 100644
--- a/test/add/git/subdir_test.dart
+++ b/test/add/git/subdir_test.dart
@@ -30,10 +30,11 @@
         ])
       ])
     ]).validate();
-
-    await d.appPackagesFile({
-      'sub': pathInCache('git/foo-${await repo.revParse('HEAD')}/subdir')
-    }).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(
+          name: 'sub',
+          path: pathInCache('git/foo-${await repo.revParse('HEAD')}/subdir')),
+    ]).validate();
 
     await d.appDir({
       'sub': {
@@ -68,9 +69,11 @@
       ])
     ]).validate();
 
-    await d.appPackagesFile({
-      'sub': pathInCache('git/foo-${await repo.revParse('HEAD')}/sub/dir')
-    }).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(
+          name: 'sub',
+          path: pathInCache('git/foo-${await repo.revParse('HEAD')}/sub/dir')),
+    ]).validate();
 
     await d.appDir({
       'sub': {
diff --git a/test/add/hosted/non_default_pub_server_test.dart b/test/add/hosted/non_default_pub_server_test.dart
index c3f7afa..787bf32 100644
--- a/test/add/hosted/non_default_pub_server_test.dart
+++ b/test/add/hosted/non_default_pub_server_test.dart
@@ -27,7 +27,9 @@
 
     await d.cacheDir({'foo': '1.2.3'}, port: server.port).validate();
 
-    await d.appPackagesFile({'foo': '1.2.3'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.2.3', server: server),
+    ]).validate();
 
     await d.appDir({
       'foo': {
@@ -60,8 +62,11 @@
     await d.cacheDir({'foo': '1.2.3', 'bar': '3.2.3', 'baz': '1.3.5'},
         port: server.port).validate();
 
-    await d.appPackagesFile(
-        {'foo': '1.2.3', 'bar': '3.2.3', 'baz': '1.3.5'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.2.3', server: server),
+      d.packageConfigEntry(name: 'bar', version: '3.2.3', server: server),
+      d.packageConfigEntry(name: 'baz', version: '1.3.5', server: server),
+    ]).validate();
 
     await d.appDir({
       'foo': {
@@ -121,7 +126,9 @@
     await pubAdd(args: ['foo', '--hosted-url', url]);
 
     await d.cacheDir({'foo': '1.2.3'}, port: server.port).validate();
-    await d.appPackagesFile({'foo': '1.2.3'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.2.3', server: server),
+    ]).validate();
     await d.appDir({
       'foo': {
         'version': '^1.2.3',
@@ -148,7 +155,9 @@
     await pubAdd(args: ['foo', '--hosted-url', url]);
 
     await d.cacheDir({'foo': '1.2.3'}, port: server.port).validate();
-    await d.appPackagesFile({'foo': '1.2.3'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.2.3', server: server),
+    ]).validate();
     await d.appDir({
       'foo': {
         'version': '^1.2.3',
@@ -176,7 +185,9 @@
     await pubAdd(args: ['foo:any', '--hosted-url', url]);
 
     await d.cacheDir({'foo': '1.2.3'}, port: server.port).validate();
-    await d.appPackagesFile({'foo': '1.2.3'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.2.3', server: server),
+    ]).validate();
     await d.appDir({
       'foo': {
         'version': 'any',
diff --git a/test/add/path/absolute_path_test.dart b/test/add/path/absolute_path_test.dart
index b15ec2d..5e63679 100644
--- a/test/add/path/absolute_path_test.dart
+++ b/test/add/path/absolute_path_test.dart
@@ -20,7 +20,9 @@
 
     await pubAdd(args: ['foo', '--path', absolutePath]);
 
-    await d.appPackagesFile({'foo': absolutePath}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', path: absolutePath),
+    ]).validate();
 
     await d.appDir({
       'foo': {'path': absolutePath}
@@ -127,7 +129,9 @@
     await pubAdd(args: ['foo', '--path', absolutePath]);
 
     await d.cacheDir({'foo': '1.2.2'}).validate();
-    await d.appPackagesFile({'foo': '1.2.2'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.2.2'),
+    ]).validate();
     await d.dir(appPath, [
       d.pubspec({
         'name': 'myapp',
diff --git a/test/add/path/relative_path_test.dart b/test/add/path/relative_path_test.dart
index c02b644..e08ba3c 100644
--- a/test/add/path/relative_path_test.dart
+++ b/test/add/path/relative_path_test.dart
@@ -19,7 +19,9 @@
 
     await pubAdd(args: ['foo', '--path', '../foo']);
 
-    await d.appPackagesFile({'foo': '../foo'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', path: '../foo'),
+    ]).validate();
 
     await d.appDir({
       'foo': {'path': '../foo'}
@@ -38,7 +40,9 @@
       output: contains('Changed 1 dependency in myapp!'),
     );
 
-    await d.appPackagesFile({'foo': '../foo'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', path: '../foo'),
+    ]).validate();
 
     await d.appDir({
       'foo': {'path': '../foo'}
@@ -118,7 +122,9 @@
     await pubAdd(args: ['foo', '--path', '../foo']);
 
     await d.cacheDir({'foo': '1.2.2'}).validate();
-    await d.appPackagesFile({'foo': '1.2.2'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.2.2'),
+    ]).validate();
     await d.dir(appPath, [
       d.pubspec({
         'name': 'myapp',
diff --git a/test/add/sdk/sdk_test.dart b/test/add/sdk/sdk_test.dart
index 1a98b78..7c81727 100644
--- a/test/add/sdk/sdk_test.dart
+++ b/test/add/sdk/sdk_test.dart
@@ -42,11 +42,12 @@
           'foo': {'sdk': 'flutter'}
         }
       }),
-      d.packagesFile({
-        'myapp': '.',
-        'foo': p.join(d.sandbox, 'flutter', 'packages', 'foo'),
-        'bar': '1.0.0'
-      })
+    ]).validate();
+
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(
+          name: 'foo', path: p.join(d.sandbox, 'flutter', 'packages', 'foo')),
+      d.packageConfigEntry(name: 'bar', version: '1.0.0'),
     ]).validate();
   });
 
@@ -65,11 +66,11 @@
           'foo': {'sdk': 'flutter', 'version': '0.0.1'}
         }
       }),
-      d.packagesFile({
-        'myapp': '.',
-        'foo': p.join(d.sandbox, 'flutter', 'packages', 'foo'),
-        'bar': '1.0.0'
-      })
+    ]).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(
+          name: 'foo', path: p.join(d.sandbox, 'flutter', 'packages', 'foo')),
+      d.packageConfigEntry(name: 'bar', version: '1.0.0'),
     ]).validate();
   });
 
@@ -79,11 +80,10 @@
         args: ['baz', '--sdk', 'flutter'],
         environment: {'FLUTTER_ROOT': p.join(d.sandbox, 'flutter')});
 
-    await d.dir(appPath, [
-      d.packagesFile({
-        'myapp': '.',
-        'baz': p.join(d.sandbox, 'flutter', 'bin', 'cache', 'pkg', 'baz')
-      })
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(
+          name: 'baz',
+          path: p.join(d.sandbox, 'flutter', 'bin', 'cache', 'pkg', 'baz'))
     ]).validate();
   });
 
diff --git a/test/dependency_override_test.dart b/test/dependency_override_test.dart
index dd8dc0d..59f59e4 100644
--- a/test/dependency_override_test.dart
+++ b/test/dependency_override_test.dart
@@ -27,7 +27,9 @@
 
       await pubCommand(command);
 
-      await d.appPackagesFile({'foo': '2.0.0'}).validate();
+      await d.appPackageConfigFile([
+        d.packageConfigEntry(name: 'foo', version: '2.0.0'),
+      ]).validate();
     });
 
     test('treats override as implicit dependency', () async {
@@ -43,7 +45,9 @@
 
       await pubCommand(command);
 
-      await d.appPackagesFile({'foo': '1.0.0'}).validate();
+      await d.appPackageConfigFile([
+        d.packageConfigEntry(name: 'foo', version: '1.0.0'),
+      ]).validate();
     });
 
     test('ignores other constraints on overridden package', () async {
@@ -65,7 +69,10 @@
 
       await pubCommand(command);
 
-      await d.appPackagesFile({'foo': '2.0.0', 'bar': '1.0.0'}).validate();
+      await d.appPackageConfigFile([
+        d.packageConfigEntry(name: 'foo', version: '2.0.0'),
+        d.packageConfigEntry(name: 'bar', version: '1.0.0'),
+      ]).validate();
     });
 
     test('ignores SDK constraints', () async {
@@ -82,8 +89,9 @@
       ]).create();
 
       await pubCommand(command);
-
-      await d.appPackagesFile({'foo': '1.0.0'}).validate();
+      await d.appPackageConfigFile([
+        d.packageConfigEntry(name: 'foo', version: '1.0.0'),
+      ]).validate();
     });
 
     test('warns about overridden dependencies', () async {
diff --git a/test/dependency_services/dependency_services_test.dart b/test/dependency_services/dependency_services_test.dart
index b007a6b..7228b36 100644
--- a/test/dependency_services/dependency_services_test.dart
+++ b/test/dependency_services/dependency_services_test.dart
@@ -17,12 +17,18 @@
 
 void manifestAndLockfile(GoldenTestContext context) {
   String catFile(String filename) {
-    final contents = filterUnstableLines(
-        File(p.join(d.sandbox, appPath, filename)).readAsLinesSync());
+    final path = p.join(d.sandbox, appPath, filename);
+    if (File(path).existsSync()) {
+      final contents = filterUnstableLines(File(path).readAsLinesSync());
 
-    return '''
+      return '''
 \$ cat $filename
 ${contents.join('\n')}''';
+    } else {
+      return '''
+\$ cat $filename
+No such file $filename.''';
+    }
   }
 
   context.expectNextSection('''
@@ -76,7 +82,7 @@
   return filterUnstableLines(s.split('\n'));
 }
 
-Future<void> listReportApply(
+Future<void> _listReportApply(
   GoldenTestContext context,
   List<_PackageVersion> upgrades, {
   void Function(Map)? reportAssertions,
@@ -126,7 +132,7 @@
     ]).create();
     await pubGet();
     server.dontAllowDownloads();
-    await listReportApply(context, [
+    await _listReportApply(context, [
       _PackageVersion('foo', '2.2.3'),
       _PackageVersion('transitive', null)
     ], reportAssertions: (report) {
@@ -141,6 +147,36 @@
     });
   });
 
+  testWithGolden('No pubspec.lock', (context) async {
+    final server = (await servePackages())
+      ..serve('foo', '1.2.3', deps: {'transitive': '^1.0.0'})
+      ..serve('foo', '2.2.3')
+      ..serve('transitive', '1.0.0');
+
+    await d.git('bar.git', [d.libPubspec('bar', '1.0.0')]).create();
+
+    await d.dir(appPath, [
+      d.pubspec({
+        'name': 'app',
+        'dependencies': {
+          'foo': '^1.0.0',
+          'bar': {
+            'git': {'url': '../bar.git'},
+          },
+        },
+      })
+    ]).create();
+
+    server.dontAllowDownloads();
+    await _listReportApply(
+      context,
+      [
+        _PackageVersion('foo', '2.2.3'),
+        _PackageVersion('transitive', null),
+      ],
+    );
+  });
+
   testWithGolden('Compatible', (context) async {
     final server = (await servePackages())
       ..serve('foo', '1.2.3')
@@ -165,7 +201,7 @@
 
     server.dontAllowDownloads();
 
-    await listReportApply(context, [
+    await _listReportApply(context, [
       _PackageVersion('foo', '1.2.4'),
     ], reportAssertions: (report) {
       expect(
@@ -192,7 +228,7 @@
     await pubGet();
     server.dontAllowDownloads();
 
-    await listReportApply(context, [
+    await _listReportApply(context, [
       _PackageVersion('foo', '2.2.3'),
       _PackageVersion('transitive', '1.0.0')
     ], reportAssertions: (report) {
@@ -235,7 +271,7 @@
 
     server.dontAllowDownloads();
 
-    await listReportApply(context, [
+    await _listReportApply(context, [
       _PackageVersion('foo', '3.0.1',
           constraint: VersionConstraint.parse('^3.0.0')),
       _PackageVersion('bar', '2.0.0')
@@ -262,7 +298,7 @@
     }).create();
     await pubGet();
     server.serve('foo', '2.0.0');
-    await listReportApply(context, [
+    await _listReportApply(context, [
       _PackageVersion('foo', '2.0.0',
           constraint: VersionConstraint.parse('^2.0.0')),
     ], reportAssertions: (report) {
@@ -295,7 +331,7 @@
     final barSecondVersion = d.git('bar.git', [d.libPubspec('bar', '2.0.0')]);
     await barSecondVersion.commit();
 
-    await listReportApply(context, [
+    await _listReportApply(context, [
       _PackageVersion('foo', newRef),
     ], reportAssertions: (report) {
       expect(
diff --git a/test/descriptor.dart b/test/descriptor.dart
index 2491df4..b3f8a55 100644
--- a/test/descriptor.dart
+++ b/test/descriptor.dart
@@ -287,6 +287,23 @@
 }) =>
     PackageConfigFileDescriptor(packages, generatorVersion);
 
+Descriptor appPackageConfigFile(
+  List<PackageConfigEntry> packages, {
+  String generatorVersion = '0.1.2+3',
+}) =>
+    dir(
+      appPath,
+      [
+        packageConfigFile(
+          [
+            packageConfigEntry(name: 'myapp', path: '.'),
+            ...packages,
+          ],
+          generatorVersion: generatorVersion,
+        ),
+      ],
+    );
+
 /// Create a [PackageConfigEntry] which assumes package with [name] is either
 /// a cached package with given [version] or a path dependency at given [path].
 PackageConfigEntry packageConfigEntry({
@@ -294,6 +311,7 @@
   String? version,
   String? path,
   String? languageVersion,
+  PackageServer? server,
 }) {
   if (version != null && path != null) {
     throw ArgumentError.value(
@@ -305,7 +323,7 @@
   }
   Uri rootUri;
   if (version != null) {
-    rootUri = p.toUri(globalServer.pathInCache(name, version));
+    rootUri = p.toUri((server ?? globalServer).pathInCache(name, version));
   } else {
     rootUri = p.toUri(p.join('..', path));
   }
diff --git a/test/descriptor/packages.dart b/test/descriptor/packages.dart
index de52a80..e728f7c 100644
--- a/test/descriptor/packages.dart
+++ b/test/descriptor/packages.dart
@@ -133,7 +133,7 @@
     final packageConfigFile = File(p.join(parent ?? sandbox, name));
     await packageConfigFile.parent.create();
     await packageConfigFile.writeAsString(
-      const JsonEncoder.withIndent('  ').convert(_config.toJson()) + '\n',
+      '${const JsonEncoder.withIndent('  ').convert(_config.toJson())}\n',
     );
   }
 
@@ -156,10 +156,20 @@
 
     // Compare packages as sets to ignore ordering.
     expect(
-      config.packages.map((e) => e.toJson()).toSet(),
-      equals(_packages.map((e) => e.toJson()).toSet()),
-      reason:
-          '"packages" property in "$packageConfigFile" does not expected values',
+      config.packages,
+      _packages
+          .map(
+            (p) => isA<PackageConfigEntry>()
+                .having((p0) => p0.name, 'name', p.name)
+                .having(
+                    (p0) => p0.languageVersion,
+                    'languageVersion',
+                    // If the expected entry has no language-version we don't check it.
+                    p.languageVersion ?? anything)
+                .having((p0) => p0.rootUri, 'rootUri', p.rootUri)
+                .having((p0) => p0.packageUri, 'packageUri', p.packageUri),
+          )
+          .toSet(),
     );
 
     final expected = PackageConfig.fromJson(_config.toJson());
diff --git a/test/dev_dependency_test.dart b/test/dev_dependency_test.dart
index 88f321b..b192444 100644
--- a/test/dev_dependency_test.dart
+++ b/test/dev_dependency_test.dart
@@ -27,7 +27,10 @@
 
     await pubGet();
 
-    await d.appPackagesFile({'foo': '../foo', 'bar': '../bar'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', path: '../foo'),
+      d.packageConfigEntry(name: 'bar', path: '../bar'),
+    ]).validate();
   });
 
   test("includes dev dependency's transitive dependencies", () async {
@@ -52,7 +55,10 @@
 
     await pubGet();
 
-    await d.appPackagesFile({'foo': '../foo', 'bar': '../bar'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', path: '../foo'),
+      d.packageConfigEntry(name: 'bar', path: '../bar'),
+    ]).validate();
   });
 
   test("ignores transitive dependency's dev dependencies", () async {
@@ -78,6 +84,8 @@
 
     await pubGet();
 
-    await d.appPackagesFile({'foo': '../foo'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', path: '../foo'),
+    ]).validate();
   });
 }
diff --git a/test/downgrade/unlock_if_necessary_test.dart b/test/downgrade/unlock_if_necessary_test.dart
index 0299e27..c8b55af 100644
--- a/test/downgrade/unlock_if_necessary_test.dart
+++ b/test/downgrade/unlock_if_necessary_test.dart
@@ -19,13 +19,19 @@
 
     await pubGet();
 
-    await d.appPackagesFile({'foo': '2.0.0', 'foo_dep': '2.0.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '2.0.0'),
+      d.packageConfigEntry(name: 'foo_dep', version: '2.0.0'),
+    ]).validate();
 
     server.serve('foo', '1.0.0', deps: {'foo_dep': '<2.0.0'});
     server.serve('foo_dep', '1.0.0');
 
     await pubDowngrade(args: ['foo']);
 
-    await d.appPackagesFile({'foo': '1.0.0', 'foo_dep': '1.0.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.0.0'),
+      d.packageConfigEntry(name: 'foo_dep', version: '1.0.0'),
+    ]).validate();
   });
 }
diff --git a/test/downgrade/unlock_single_package_test.dart b/test/downgrade/unlock_single_package_test.dart
index cfd315d..1188e41 100644
--- a/test/downgrade/unlock_single_package_test.dart
+++ b/test/downgrade/unlock_single_package_test.dart
@@ -16,22 +16,34 @@
     await d.appDir({'foo': 'any', 'bar': 'any'}).create();
 
     await pubGet();
-    await d.appPackagesFile({'foo': '2.1.0', 'bar': '2.1.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '2.1.0'),
+      d.packageConfigEntry(name: 'bar', version: '2.1.0'),
+    ]).validate();
 
     server.serve('foo', '1.0.0', deps: {'bar': 'any'});
     server.serve('bar', '1.0.0');
 
     await pubDowngrade(args: ['bar']);
-    await d.appPackagesFile({'foo': '2.1.0', 'bar': '2.1.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '2.1.0'),
+      d.packageConfigEntry(name: 'bar', version: '2.1.0'),
+    ]).validate();
 
     server.serve('foo', '2.0.0', deps: {'bar': 'any'});
     server.serve('bar', '2.0.0');
 
     await pubDowngrade(args: ['bar']);
-    await d.appPackagesFile({'foo': '2.1.0', 'bar': '2.0.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '2.1.0'),
+      d.packageConfigEntry(name: 'bar', version: '2.0.0'),
+    ]).validate();
 
     await pubDowngrade();
-    await d.appPackagesFile({'foo': '1.0.0', 'bar': '1.0.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.0.0'),
+      d.packageConfigEntry(name: 'bar', version: '1.0.0'),
+    ]).validate();
   });
 
   test('will not downgrade below constraint #2629', () async {
@@ -43,11 +55,14 @@
     await d.appDir({'foo': '^2.0.0'}).create();
 
     await pubGet();
-
-    await d.appPackagesFile({'foo': '2.1.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '2.1.0'),
+    ]).validate();
 
     await pubDowngrade(args: ['foo']);
 
-    await d.appPackagesFile({'foo': '2.0.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '2.0.0'),
+    ]).validate();
   });
 }
diff --git a/test/embedding/get_executable_for_command_test.dart b/test/embedding/get_executable_for_command_test.dart
index 0edfa72..7f395a7 100644
--- a/test/embedding/get_executable_for_command_test.dart
+++ b/test/embedding/get_executable_for_command_test.dart
@@ -23,7 +23,7 @@
   errorMessage,
   CommandResolutionIssue? issue,
 }) async {
-  final _cachePath = getPubTestEnvironment()['PUB_CACHE'];
+  final cachePath = getPubTestEnvironment()['PUB_CACHE'];
   final oldVerbosity = log.verbosity;
   log.verbosity = log.Verbosity.none;
   if (executable == null) {
@@ -31,7 +31,7 @@
       () => getExecutableForCommand(
         command,
         root: root,
-        pubCacheDir: _cachePath,
+        pubCacheDir: cachePath,
         allowSnapshot: allowSnapshot,
       ),
       throwsA(
@@ -44,7 +44,7 @@
     final e = await getExecutableForCommand(
       command,
       root: root,
-      pubCacheDir: _cachePath,
+      pubCacheDir: cachePath,
       allowSnapshot: allowSnapshot,
     );
     expect(
diff --git a/test/get/git/path_test.dart b/test/get/git/path_test.dart
index d26d9cb..d9c9195 100644
--- a/test/get/git/path_test.dart
+++ b/test/get/git/path_test.dart
@@ -39,9 +39,11 @@
       ])
     ]).validate();
 
-    await d.appPackagesFile({
-      'sub': pathInCache('git/foo-${await repo.revParse('HEAD')}/subdir')
-    }).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(
+          name: 'sub',
+          path: pathInCache('git/foo-${await repo.revParse('HEAD')}/subdir')),
+    ]).validate();
   });
 
   test('depends on a package in a deep subdirectory', () async {
@@ -73,9 +75,12 @@
       ])
     ]).validate();
 
-    await d.appPackagesFile({
-      'sub': pathInCache('git/foo-${await repo.revParse('HEAD')}/sub/dir%25')
-    }).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(
+          name: 'sub',
+          path:
+              pathInCache('git/foo-${await repo.revParse('HEAD')}/sub/dir%25')),
+    ]).validate();
 
     final lockFile = LockFile.load(
         p.join(d.sandbox, appPath, 'pubspec.lock'), SystemCache().sources);
@@ -198,9 +203,12 @@
       ])
     ]).validate();
 
-    await d.appPackagesFile({
-      'sub': pathInCache('git/foo-${await repo.revParse('HEAD')}/sub/dir%25')
-    }).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(
+          name: 'sub',
+          path:
+              pathInCache('git/foo-${await repo.revParse('HEAD')}/sub/dir%25')),
+    ]).validate();
 
     final lockFile = LockFile.load(
         p.join(d.sandbox, appPath, 'pubspec.lock'), SystemCache().sources);
@@ -244,10 +252,14 @@
       ])
     ]).validate();
 
-    await d.appPackagesFile({
-      'sub1': pathInCache('git/foo-${await repo.revParse('HEAD')}/subdir1'),
-      'sub2': pathInCache('git/foo-${await repo.revParse('HEAD')}/subdir2')
-    }).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(
+          name: 'sub1',
+          path: pathInCache('git/foo-${await repo.revParse('HEAD')}/subdir1')),
+      d.packageConfigEntry(
+          name: 'sub2',
+          path: pathInCache('git/foo-${await repo.revParse('HEAD')}/subdir2')),
+    ]).validate();
   });
 
   test('depends on packages in the same subdirectory at different revisions',
@@ -292,9 +304,11 @@
       ])
     ]).validate();
 
-    await d.appPackagesFile({
-      'sub1': pathInCache('git/foo-$oldRevision/subdir'),
-      'sub2': pathInCache('git/foo-$newRevision/subdir')
-    }).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(
+          name: 'sub1', path: pathInCache('git/foo-$oldRevision/subdir')),
+      d.packageConfigEntry(
+          name: 'sub2', path: pathInCache('git/foo-$newRevision/subdir')),
+    ]).validate();
   });
 }
diff --git a/test/get/hosted/avoid_network_requests_test.dart b/test/get/hosted/avoid_network_requests_test.dart
index 8778ae6..c62fa7c 100644
--- a/test/get/hosted/avoid_network_requests_test.dart
+++ b/test/get/hosted/avoid_network_requests_test.dart
@@ -31,8 +31,10 @@
 
     // Run the solver again.
     await pubGet();
-
-    await d.appPackagesFile({'foo': '1.2.0', 'bar': '1.2.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.2.0'),
+      d.packageConfigEntry(name: 'bar', version: '1.2.0'),
+    ]).validate();
 
     // The get should not have done any network requests since the lock file is
     // up to date.
diff --git a/test/get/hosted/cached_pubspec_test.dart b/test/get/hosted/cached_pubspec_test.dart
index beb4e16..fa9decc 100644
--- a/test/get/hosted/cached_pubspec_test.dart
+++ b/test/get/hosted/cached_pubspec_test.dart
@@ -22,7 +22,9 @@
     server.requestedPaths.clear();
 
     await d.cacheDir({'foo': '1.2.3'}).validate();
-    await d.appPackagesFile({'foo': '1.2.3'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.2.3'),
+    ]).validate();
 
     // Run the solver again now that it's cached.
     await pubGet();
diff --git a/test/get/hosted/do_not_upgrade_on_removed_constraints_test.dart b/test/get/hosted/do_not_upgrade_on_removed_constraints_test.dart
index 00b2444..44fe075 100644
--- a/test/get/hosted/do_not_upgrade_on_removed_constraints_test.dart
+++ b/test/get/hosted/do_not_upgrade_on_removed_constraints_test.dart
@@ -21,13 +21,19 @@
 
     await pubGet();
 
-    await d.appPackagesFile(
-        {'foo': '1.0.0', 'bar': '1.0.0', 'shared_dep': '1.0.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.0.0'),
+      d.packageConfigEntry(name: 'bar', version: '1.0.0'),
+      d.packageConfigEntry(name: 'shared_dep', version: '1.0.0'),
+    ]).validate();
 
     await d.appDir({'foo': 'any'}).create();
 
     await pubGet();
 
-    await d.appPackagesFile({'foo': '1.0.0', 'shared_dep': '1.0.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.0.0'),
+      d.packageConfigEntry(name: 'shared_dep', version: '1.0.0'),
+    ]).validate();
   });
 }
diff --git a/test/get/hosted/does_no_network_requests_when_possible_test.dart b/test/get/hosted/does_no_network_requests_when_possible_test.dart
index ea1f4a7..4324776 100644
--- a/test/get/hosted/does_no_network_requests_when_possible_test.dart
+++ b/test/get/hosted/does_no_network_requests_when_possible_test.dart
@@ -27,7 +27,9 @@
     await pubGet();
 
     await d.cacheDir({'foo': '1.2.0'}).validate();
-    await d.appPackagesFile({'foo': '1.2.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.2.0'),
+    ]).validate();
 
     // The get should not have done any network requests since the lock file is
     // up to date.
diff --git a/test/get/hosted/get_stress_test.dart b/test/get/hosted/get_stress_test.dart
index 86a8da9..026c0c1 100644
--- a/test/get/hosted/get_stress_test.dart
+++ b/test/get/hosted/get_stress_test.dart
@@ -26,10 +26,10 @@
       'foo': '1.2.3',
       for (var i = 0; i < 20; i++) 'pkg$i': '1.$i.0',
     }).validate();
-
-    await d.appPackagesFile({
-      'foo': '1.2.3',
-      for (var i = 0; i < 20; i++) 'pkg$i': '1.$i.0',
-    }).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.2.3'),
+      for (var i = 0; i < 20; i++)
+        d.packageConfigEntry(name: 'pkg$i', version: '1.$i.0')
+    ]).validate();
   });
 }
diff --git a/test/get/hosted/get_test.dart b/test/get/hosted/get_test.dart
index 30dd4b8..f0270a3 100644
--- a/test/get/hosted/get_test.dart
+++ b/test/get/hosted/get_test.dart
@@ -21,7 +21,9 @@
     await pubGet();
 
     await d.cacheDir({'foo': '1.2.3'}).validate();
-    await d.appPackagesFile({'foo': '1.2.3'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.2.3'),
+    ]).validate();
   });
 
   test('URL encodes the package name', () async {
@@ -57,7 +59,9 @@
     await pubGet();
 
     await d.cacheDir({'foo': '1.2.3'}, port: server.port).validate();
-    await d.appPackagesFile({'foo': '1.2.3'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.2.3', server: server),
+    ]).validate();
   });
 
   group('categorizes dependency types in the lockfile', () {
diff --git a/test/get/hosted/get_transitive_test.dart b/test/get/hosted/get_transitive_test.dart
index 74635bc..0ae18ac 100644
--- a/test/get/hosted/get_transitive_test.dart
+++ b/test/get/hosted/get_transitive_test.dart
@@ -20,6 +20,9 @@
     await pubGet();
 
     await d.cacheDir({'foo': '1.2.3', 'bar': '2.0.4'}).validate();
-    await d.appPackagesFile({'foo': '1.2.3', 'bar': '2.0.4'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.2.3'),
+      d.packageConfigEntry(name: 'bar', version: '2.0.4'),
+    ]).validate();
   });
 }
diff --git a/test/get/hosted/gets_a_package_with_busted_dev_dependencies_test.dart b/test/get/hosted/gets_a_package_with_busted_dev_dependencies_test.dart
index 0d75f95..585cfba 100644
--- a/test/get/hosted/gets_a_package_with_busted_dev_dependencies_test.dart
+++ b/test/get/hosted/gets_a_package_with_busted_dev_dependencies_test.dart
@@ -24,6 +24,8 @@
     await pubGet();
 
     await d.cacheDir({'foo': '1.2.3'}).validate();
-    await d.appPackagesFile({'foo': '1.2.3'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.2.3'),
+    ]).validate();
   });
 }
diff --git a/test/get/hosted/resolve_constraints_test.dart b/test/get/hosted/resolve_constraints_test.dart
index c9c73d2..466902e 100644
--- a/test/get/hosted/resolve_constraints_test.dart
+++ b/test/get/hosted/resolve_constraints_test.dart
@@ -22,8 +22,10 @@
 
     await d
         .cacheDir({'foo': '1.2.3', 'bar': '2.3.4', 'baz': '2.0.4'}).validate();
-
-    await d.appPackagesFile(
-        {'foo': '1.2.3', 'bar': '2.3.4', 'baz': '2.0.4'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.2.3'),
+      d.packageConfigEntry(name: 'bar', version: '2.3.4'),
+      d.packageConfigEntry(name: 'baz', version: '2.0.4'),
+    ]).validate();
   });
 }
diff --git a/test/get/hosted/resolve_with_retracted_package_versions_test.dart b/test/get/hosted/resolve_with_retracted_package_versions_test.dart
index efd3a0a..d9b33a4 100644
--- a/test/get/hosted/resolve_with_retracted_package_versions_test.dart
+++ b/test/get/hosted/resolve_with_retracted_package_versions_test.dart
@@ -23,7 +23,10 @@
     await pubGet();
 
     await d.cacheDir({'foo': '1.0.0', 'bar': '1.0.0'}).validate();
-    await d.appPackagesFile({'foo': '1.0.0', 'bar': '1.0.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.0.0'),
+      d.packageConfigEntry(name: 'bar', version: '1.0.0'),
+    ]).validate();
   });
 
   test('Error when the only available package version is retracted', () async {
@@ -53,22 +56,34 @@
 
     await pubGet();
     await d.cacheDir({'foo': '1.0.0', 'bar': '1.1.0'}).validate();
-    await d.appPackagesFile({'foo': '1.0.0', 'bar': '1.1.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.0.0'),
+      d.packageConfigEntry(name: 'bar', version: '1.1.0'),
+    ]).validate();
 
     server.retractPackageVersion('bar', '1.1.0');
     await pubUpgrade();
     await d.cacheDir({'foo': '1.0.0', 'bar': '1.1.0'}).validate();
-    await d.appPackagesFile({'foo': '1.0.0', 'bar': '1.1.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.0.0'),
+      d.packageConfigEntry(name: 'bar', version: '1.1.0'),
+    ]).validate();
 
     server.serve('bar', '2.0.0');
     await pubUpgrade();
     await d.cacheDir({'foo': '1.0.0', 'bar': '1.1.0'}).validate();
-    await d.appPackagesFile({'foo': '1.0.0', 'bar': '1.1.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.0.0'),
+      d.packageConfigEntry(name: 'bar', version: '1.1.0'),
+    ]).validate();
 
     server.serve('bar', '1.2.0');
     await pubUpgrade();
     await d.cacheDir({'foo': '1.0.0', 'bar': '1.2.0'}).validate();
-    await d.appPackagesFile({'foo': '1.0.0', 'bar': '1.2.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.0.0'),
+      d.packageConfigEntry(name: 'bar', version: '1.2.0'),
+    ]).validate();
   });
 
   test('Offline versions of pub commands also handle retracted packages',
@@ -102,7 +117,10 @@
     await pubUpgrade(args: ['--offline']);
 
     // We choose bar 1.1.0 since we already have it in pubspec.lock
-    await d.appPackagesFile({'foo': '1.0.0', 'bar': '1.1.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.0.0'),
+      d.packageConfigEntry(name: 'bar', version: '1.1.0'),
+    ]).validate();
 
     // Delete lockfile so that retracted versions are not considered.
     final lockFile = p.join(d.sandbox, appPath, 'pubspec.lock');
@@ -110,7 +128,10 @@
     deleteEntry(lockFile);
 
     await pubGet(args: ['--offline']);
-    await d.appPackagesFile({'foo': '1.0.0', 'bar': '1.0.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.0.0'),
+      d.packageConfigEntry(name: 'bar', version: '1.0.0'),
+    ]).validate();
   });
 
   test('Allow retracted version when pinned in dependency_overrides', () async {
@@ -130,7 +151,9 @@
     server.retractPackageVersion('foo', '2.0.0');
 
     await pubGet();
-    await d.appPackagesFile({'foo': '2.0.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '2.0.0'),
+    ]).validate();
   });
 
   test('Prefer retracted version in dependency_overrides over pubspec.lock',
@@ -147,7 +170,9 @@
     server.retractPackageVersion('foo', '3.0.0');
 
     await pubUpgrade();
-    await d.appPackagesFile({'foo': '3.0.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '3.0.0'),
+    ]).validate();
 
     await d.dir(appPath, [
       d.pubspec({
@@ -158,6 +183,8 @@
     ]).create();
 
     await pubUpgrade();
-    await d.appPackagesFile({'foo': '2.0.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '2.0.0'),
+    ]).validate();
   });
 }
diff --git a/test/get/hosted/stay_locked_if_compatible_test.dart b/test/get/hosted/stay_locked_if_compatible_test.dart
index bb5e63a..df04579 100644
--- a/test/get/hosted/stay_locked_if_compatible_test.dart
+++ b/test/get/hosted/stay_locked_if_compatible_test.dart
@@ -17,8 +17,9 @@
     await d.appDir({'foo': 'any'}).create();
 
     await pubGet();
-
-    await d.appPackagesFile({'foo': '1.0.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.0.0'),
+    ]).validate();
 
     server.serve('foo', '1.0.1');
 
@@ -26,6 +27,8 @@
 
     await pubGet();
 
-    await d.appPackagesFile({'foo': '1.0.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.0.0'),
+    ]).validate();
   });
 }
diff --git a/test/get/hosted/stay_locked_if_new_is_satisfied_test.dart b/test/get/hosted/stay_locked_if_new_is_satisfied_test.dart
index dbe6039..c75c435 100644
--- a/test/get/hosted/stay_locked_if_new_is_satisfied_test.dart
+++ b/test/get/hosted/stay_locked_if_new_is_satisfied_test.dart
@@ -19,9 +19,11 @@
     await d.appDir({'foo': 'any'}).create();
 
     await pubGet();
-
-    await d.appPackagesFile(
-        {'foo': '1.0.0', 'bar': '1.0.0', 'baz': '1.0.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.0.0'),
+      d.packageConfigEntry(name: 'bar', version: '1.0.0'),
+      d.packageConfigEntry(name: 'baz', version: '1.0.0'),
+    ]).validate();
 
     server.serve('foo', '2.0.0', deps: {'bar': '<3.0.0'});
     server.serve('bar', '2.0.0', deps: {'baz': '<3.0.0'});
@@ -32,11 +34,11 @@
 
     await pubGet();
 
-    await d.appPackagesFile({
-      'foo': '1.0.0',
-      'bar': '1.0.0',
-      'baz': '1.0.0',
-      'newdep': '2.0.0'
-    }).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.0.0'),
+      d.packageConfigEntry(name: 'bar', version: '1.0.0'),
+      d.packageConfigEntry(name: 'baz', version: '1.0.0'),
+      d.packageConfigEntry(name: 'newdep', version: '2.0.0'),
+    ]).validate();
   });
 }
diff --git a/test/get/hosted/stay_locked_test.dart b/test/get/hosted/stay_locked_test.dart
index 134e8b5..8b819ea 100644
--- a/test/get/hosted/stay_locked_test.dart
+++ b/test/get/hosted/stay_locked_test.dart
@@ -20,8 +20,9 @@
 
     // This should lock the foo dependency to version 1.0.0.
     await pubGet();
-
-    await d.appPackagesFile({'foo': '1.0.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.0.0'),
+    ]).validate();
 
     // Delete the .dart_tool/package_config.json file to simulate a new checkout of the application.
     deleteEntry(path.join(d.sandbox, packageConfigFilePath));
@@ -32,6 +33,8 @@
     // This shouldn't upgrade the foo dependency due to the lockfile.
     await pubGet();
 
-    await d.appPackagesFile({'foo': '1.0.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.0.0'),
+    ]).validate();
   });
 }
diff --git a/test/get/hosted/unlock_if_incompatible_test.dart b/test/get/hosted/unlock_if_incompatible_test.dart
index 11bf909..007231e 100644
--- a/test/get/hosted/unlock_if_incompatible_test.dart
+++ b/test/get/hosted/unlock_if_incompatible_test.dart
@@ -18,12 +18,16 @@
 
     await pubGet();
 
-    await d.appPackagesFile({'foo': '1.0.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.0.0'),
+    ]).validate();
     server.serve('foo', '1.0.1');
     await d.appDir({'foo': '>1.0.0'}).create();
 
     await pubGet();
 
-    await d.appPackagesFile({'foo': '1.0.1'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.0.1'),
+    ]).validate();
   });
 }
diff --git a/test/get/hosted/unlock_if_new_is_unsatisfied_test.dart b/test/get/hosted/unlock_if_new_is_unsatisfied_test.dart
index 1cedc5a..ae6a4e6 100644
--- a/test/get/hosted/unlock_if_new_is_unsatisfied_test.dart
+++ b/test/get/hosted/unlock_if_new_is_unsatisfied_test.dart
@@ -22,12 +22,12 @@
 
     await pubGet();
 
-    await d.appPackagesFile({
-      'foo': '1.0.0',
-      'bar': '1.0.0',
-      'baz': '1.0.0',
-      'qux': '1.0.0'
-    }).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.0.0'),
+      d.packageConfigEntry(name: 'bar', version: '1.0.0'),
+      d.packageConfigEntry(name: 'baz', version: '1.0.0'),
+      d.packageConfigEntry(name: 'qux', version: '1.0.0'),
+    ]).validate();
 
     server.serve('foo', '2.0.0', deps: {'bar': '<3.0.0'});
     server.serve('bar', '2.0.0', deps: {'baz': '<3.0.0'});
@@ -39,12 +39,12 @@
 
     await pubGet();
 
-    await d.appPackagesFile({
-      'foo': '2.0.0',
-      'bar': '2.0.0',
-      'baz': '2.0.0',
-      'qux': '1.0.0',
-      'newdep': '2.0.0'
-    }).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '2.0.0'),
+      d.packageConfigEntry(name: 'bar', version: '2.0.0'),
+      d.packageConfigEntry(name: 'baz', version: '2.0.0'),
+      d.packageConfigEntry(name: 'qux', version: '1.0.0'),
+      d.packageConfigEntry(name: 'newdep', version: '2.0.0'),
+    ]).validate();
   });
 }
diff --git a/test/get/hosted/unlock_if_version_doesnt_exist_test.dart b/test/get/hosted/unlock_if_version_doesnt_exist_test.dart
index 7eb4246..e4cc01b 100644
--- a/test/get/hosted/unlock_if_version_doesnt_exist_test.dart
+++ b/test/get/hosted/unlock_if_version_doesnt_exist_test.dart
@@ -17,7 +17,9 @@
 
     await d.appDir({'foo': 'any'}).create();
     await pubGet();
-    await d.appPackagesFile({'foo': '1.0.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.0.0'),
+    ]).validate();
 
     deleteEntry(p.join(d.sandbox, cachePath));
 
@@ -25,6 +27,8 @@
     server.serve('foo', '1.0.1');
 
     await pubGet();
-    await d.appPackagesFile({'foo': '1.0.1'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.0.1'),
+    ]).validate();
   });
 }
diff --git a/test/get/package_name_test.dart b/test/get/package_name_test.dart
index 46ca386..b1328cc 100644
--- a/test/get/package_name_test.dart
+++ b/test/get/package_name_test.dart
@@ -56,7 +56,8 @@
     await pubGet();
 
     await d.dir(appPath, [
-      d.packagesFile({'foo.bar.baz': '.'}),
+      d.packageConfigFile(
+          [d.packageConfigEntry(name: 'foo.bar.baz', path: '.')])
     ]).validate();
   });
 }
diff --git a/test/get/path/absolute_path_test.dart b/test/get/path/absolute_path_test.dart
index 0fc987a..e1897e9 100644
--- a/test/get/path/absolute_path_test.dart
+++ b/test/get/path/absolute_path_test.dart
@@ -21,6 +21,8 @@
 
     await pubGet();
 
-    await d.appPackagesFile({'foo': path.join(d.sandbox, 'foo')}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', path: path.join(d.sandbox, 'foo')),
+    ]).validate();
   });
 }
diff --git a/test/get/path/absolute_symlink_test.dart b/test/get/path/absolute_symlink_test.dart
index 1a2d912..83d3433 100644
--- a/test/get/path/absolute_symlink_test.dart
+++ b/test/get/path/absolute_symlink_test.dart
@@ -24,8 +24,8 @@
 
     await pubGet();
 
-    await d.dir(appPath, [
-      d.packagesFile({'myapp': '.', 'foo': fooPath})
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', path: fooPath),
     ]).validate();
 
     await d.dir('moved').create();
@@ -35,9 +35,9 @@
     renameInSandbox(appPath, path.join('moved', appPath));
 
     await d.dir('moved', [
-      d.dir(appPath, [
-        d.packagesFile({'myapp': '.', 'foo': fooPath})
-      ])
+      d.appPackageConfigFile([
+        d.packageConfigEntry(name: 'foo', path: fooPath),
+      ]),
     ]).validate();
   });
 }
diff --git a/test/get/path/relative_path_test.dart b/test/get/path/relative_path_test.dart
index ff19c6f..3a7fb8f 100644
--- a/test/get/path/relative_path_test.dart
+++ b/test/get/path/relative_path_test.dart
@@ -27,7 +27,9 @@
 
     await pubGet();
 
-    await d.appPackagesFile({'foo': '../foo'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', path: '../foo'),
+    ]).validate();
   });
 
   test('path is relative to containing pubspec', () async {
@@ -49,8 +51,10 @@
 
     await pubGet();
 
-    await d.appPackagesFile(
-        {'foo': '../relative/foo', 'bar': '../relative/bar'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', path: '../relative/foo'),
+      d.packageConfigEntry(name: 'bar', path: '../relative/bar'),
+    ]).validate();
   });
 
   test('path is relative to containing pubspec when using --directory',
@@ -76,9 +80,10 @@
         workingDirectory: d.sandbox,
         output: contains('Changed 2 dependencies in myapp!'));
 
-    await d.appPackagesFile(
-      {'foo': '../relative/foo', 'bar': '../relative/bar'},
-    ).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', path: '../relative/foo'),
+      d.packageConfigEntry(name: 'bar', path: '../relative/bar'),
+    ]).validate();
   });
 
   test('relative path preserved in the lockfile', () async {
diff --git a/test/get/path/relative_symlink_test.dart b/test/get/path/relative_symlink_test.dart
index af7185b..9136a34 100644
--- a/test/get/path/relative_symlink_test.dart
+++ b/test/get/path/relative_symlink_test.dart
@@ -28,8 +28,8 @@
 
     await pubGet();
 
-    await d.dir(appPath, [
-      d.packagesFile({'myapp': '.', 'foo': '../foo'})
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', path: '../foo'),
     ]).validate();
 
     await d.dir('moved').create();
@@ -41,8 +41,8 @@
     renameInSandbox(appPath, path.join('moved', appPath));
 
     await d.dir('moved', [
-      d.dir(appPath, [
-        d.packagesFile({'myapp': '.', 'foo': '../foo'})
+      d.appPackageConfigFile([
+        d.packageConfigEntry(name: 'foo', path: '../foo'),
       ])
     ]).validate();
   });
diff --git a/test/get/path/shared_dependency_symlink_test.dart b/test/get/path/shared_dependency_symlink_test.dart
index 46a78c6..d450838 100644
--- a/test/get/path/shared_dependency_symlink_test.dart
+++ b/test/get/path/shared_dependency_symlink_test.dart
@@ -40,13 +40,10 @@
 
     await pubGet();
 
-    await d.dir(appPath, [
-      d.packagesFile({
-        'myapp': '.',
-        'foo': '../foo',
-        'bar': '../bar',
-        'shared': '../shared'
-      })
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', path: '../foo'),
+      d.packageConfigEntry(name: 'bar', path: '../bar'),
+      d.packageConfigEntry(name: 'shared', path: '../shared'),
     ]).validate();
   });
 }
diff --git a/test/get/path/shared_dependency_test.dart b/test/get/path/shared_dependency_test.dart
index 7072221..3ef91ae 100644
--- a/test/get/path/shared_dependency_test.dart
+++ b/test/get/path/shared_dependency_test.dart
@@ -35,8 +35,11 @@
 
     await pubGet();
 
-    await d.appPackagesFile(
-        {'foo': '../foo', 'bar': '../bar', 'shared': '../shared'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', path: '../foo'),
+      d.packageConfigEntry(name: 'bar', path: '../bar'),
+      d.packageConfigEntry(name: 'shared', path: '../shared'),
+    ]).validate();
   });
 
   test('shared dependency with paths that normalize the same', () async {
@@ -66,7 +69,10 @@
 
     await pubGet();
 
-    await d.appPackagesFile(
-        {'foo': '../foo', 'bar': '../bar', 'shared': '../shared'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', path: '../foo'),
+      d.packageConfigEntry(name: 'bar', path: '../bar'),
+      d.packageConfigEntry(name: 'shared', path: '../shared'),
+    ]).validate();
   });
 }
diff --git a/test/get/sdk_constraint_required_test.dart b/test/get/sdk_constraint_required_test.dart
index 1b6fbc5..3f1e5c0 100644
--- a/test/get/sdk_constraint_required_test.dart
+++ b/test/get/sdk_constraint_required_test.dart
@@ -25,8 +25,8 @@
       d.nothing('pubspec.lock'),
       // The "packages" directory should not have been generated.
       d.nothing('packages'),
-      // The ".packages" file should not have been created.
-      d.nothing('.packages'),
+      // The package config file should not have been created.
+      d.nothing('.dart_tool/package_config.json'),
     ]).validate();
   });
 }
diff --git a/test/get/switch_source_test.dart b/test/get/switch_source_test.dart
index ffbf8b4..7e56652 100644
--- a/test/get/switch_source_test.dart
+++ b/test/get/switch_source_test.dart
@@ -21,11 +21,15 @@
 
     await pubGet();
 
-    await d.appPackagesFile({'foo': '../foo'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', path: '../foo'),
+    ]).validate();
     await d.appDir({'foo': 'any'}).create();
 
     await pubGet();
 
-    await d.appPackagesFile({'foo': '1.2.3'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.2.3'),
+    ]).validate();
   });
 }
diff --git a/test/golden_file.dart b/test/golden_file.dart
index 6a88dd9..c4baefe 100644
--- a/test/golden_file.dart
+++ b/test/golden_file.dart
@@ -45,7 +45,7 @@
   late File _goldenFile;
   late String _header;
   final _results = <String>[];
-  late bool _goldenFileExists;
+  late bool _shouldRegenerateGolden;
   bool _generatedNewData = false; // track if new data is generated
   int _nextSectionIndex = 0;
 
@@ -60,17 +60,20 @@
       'goldens',
       rel,
       // Sanitize the name, and add .txt
-      _testName.replaceAll(RegExp(r'[<>:"/\|?*%#]'), '~') + '.txt',
+      '${_testName.replaceAll(RegExp(r'[<>:"/\|?*%#]'), '~')}.txt',
     );
     _goldenFile = File(_goldenFilePath);
     _header = '# GENERATED BY: ${p.relative(_currentTestFile)}\n\n';
   }
 
   void _readGoldenFile() {
-    _goldenFileExists = _goldenFile.existsSync();
-
-    // Read the golden file for this test
-    if (_goldenFileExists) {
+    if (RegExp(r'^1|(?:true)$', caseSensitive: false)
+            .hasMatch(Platform.environment['_PUB_TEST_WRITE_GOLDEN'] ?? '') ||
+        !_goldenFile.existsSync()) {
+      _shouldRegenerateGolden = true;
+    } else {
+      _shouldRegenerateGolden = false;
+      // Read the golden file for this test
       var text = _goldenFile.readAsStringSync().replaceAll('\r\n', '\n');
       // Strip header line
       if (text.startsWith('#') && text.contains('\n\n')) {
@@ -82,7 +85,7 @@
 
   /// Expect section [sectionIndex] to match [actual].
   void _expectSection(int sectionIndex, String actual) {
-    if (_goldenFileExists &&
+    if (!_shouldRegenerateGolden &&
         _results.length > sectionIndex &&
         _results[sectionIndex].isNotEmpty) {
       expect(
diff --git a/test/hosted/offline_test.dart b/test/hosted/offline_test.dart
index 6482656..ca3dc80 100644
--- a/test/hosted/offline_test.dart
+++ b/test/hosted/offline_test.dart
@@ -44,8 +44,10 @@
       }
 
       await pubCommand(command, args: ['--offline'], warning: warning);
-
-      await d.appPackagesFile({'foo': '1.2.3', 'bar': '1.2.3'}).validate();
+      await d.appPackageConfigFile([
+        d.packageConfigEntry(name: 'foo', version: '1.2.3'),
+        d.packageConfigEntry(name: 'bar', version: '1.2.3'),
+      ]).validate();
     });
 
     test('supports prerelease versions', () async {
@@ -66,7 +68,9 @@
 
       await pubCommand(command, args: ['--offline'], warning: warning);
 
-      await d.appPackagesFile({'foo': '1.2.3-alpha.1'}).validate();
+      await d.appPackageConfigFile([
+        d.packageConfigEntry(name: 'foo', version: '1.2.3-alpha.1'),
+      ]).validate();
     });
 
     test('fails gracefully if a dependency is not cached', () async {
@@ -143,7 +147,9 @@
 
       await pubCommand(command, args: ['--offline']);
 
-      await d.appPackagesFile({'foo': '1.2.3'}).validate();
+      await d.appPackageConfigFile([
+        d.packageConfigEntry(name: 'foo', version: '1.2.3'),
+      ]).validate();
     });
 
     test('skips invalid cached versions', () async {
@@ -164,7 +170,9 @@
 
       await pubCommand(command, args: ['--offline']);
 
-      await d.appPackagesFile({'foo': '1.2.2'}).validate();
+      await d.appPackageConfigFile([
+        d.packageConfigEntry(name: 'foo', version: '1.2.2'),
+      ]).validate();
     });
 
     test('skips invalid locked versions', () async {
@@ -186,7 +194,9 @@
 
       await pubCommand(command, args: ['--offline']);
 
-      await d.appPackagesFile({'foo': '1.2.2'}).validate();
+      await d.appPackageConfigFile([
+        d.packageConfigEntry(name: 'foo', version: '1.2.2'),
+      ]).validate();
     });
   });
 }
diff --git a/test/hosted/remove_removed_dependency_test.dart b/test/hosted/remove_removed_dependency_test.dart
index bb2e091..a0446c0 100644
--- a/test/hosted/remove_removed_dependency_test.dart
+++ b/test/hosted/remove_removed_dependency_test.dart
@@ -17,14 +17,18 @@
       await d.appDir({'foo': 'any', 'bar': 'any'}).create();
 
       await pubCommand(command);
-
-      await d.appPackagesFile({'foo': '1.0.0', 'bar': '1.0.0'}).validate();
+      await d.appPackageConfigFile([
+        d.packageConfigEntry(name: 'foo', version: '1.0.0'),
+        d.packageConfigEntry(name: 'bar', version: '1.0.0'),
+      ]).validate();
 
       await d.appDir({'foo': 'any'}).create();
 
       await pubCommand(command);
 
-      await d.appPackagesFile({'foo': '1.0.0'}).validate();
+      await d.appPackageConfigFile([
+        d.packageConfigEntry(name: 'foo', version: '1.0.0'),
+      ]).validate();
     });
   });
 }
diff --git a/test/hosted/remove_removed_transitive_dependency_test.dart b/test/hosted/remove_removed_transitive_dependency_test.dart
index ac3a452..182ec3f 100644
--- a/test/hosted/remove_removed_transitive_dependency_test.dart
+++ b/test/hosted/remove_removed_transitive_dependency_test.dart
@@ -21,20 +21,21 @@
       await d.appDir({'foo': 'any', 'bar': 'any'}).create();
 
       await pubCommand(command);
-
-      await d.appPackagesFile({
-        'foo': '1.0.0',
-        'bar': '1.0.0',
-        'shared_dep': '1.0.0',
-        'bar_dep': '1.0.0',
-      }).validate();
+      await d.appPackageConfigFile([
+        d.packageConfigEntry(name: 'foo', version: '1.0.0'),
+        d.packageConfigEntry(name: 'bar', version: '1.0.0'),
+        d.packageConfigEntry(name: 'shared_dep', version: '1.0.0'),
+        d.packageConfigEntry(name: 'bar_dep', version: '1.0.0'),
+      ]).validate();
 
       await d.appDir({'foo': 'any'}).create();
 
       await pubCommand(command);
 
-      await d
-          .appPackagesFile({'foo': '1.0.0', 'shared_dep': '1.0.0'}).validate();
+      await d.appPackageConfigFile([
+        d.packageConfigEntry(name: 'foo', version: '1.0.0'),
+        d.packageConfigEntry(name: 'shared_dep', version: '1.0.0'),
+      ]).validate();
     });
   });
 }
diff --git a/test/hosted/will_normalize_hosted_url_test.dart b/test/hosted/will_normalize_hosted_url_test.dart
index 6cc3888..904d3d8 100644
--- a/test/hosted/will_normalize_hosted_url_test.dart
+++ b/test/hosted/will_normalize_hosted_url_test.dart
@@ -42,7 +42,7 @@
       await d.dir(appPath, [
         d.appPubspec({
           'foo': {
-            'hosted': {'name': 'foo', 'url': globalServer.url + '/'},
+            'hosted': {'name': 'foo', 'url': '${globalServer.url}/'},
           },
         }),
       ]).create();
@@ -65,7 +65,7 @@
       await d.dir(appPath, [
         d.appPubspec({
           'foo': {
-            'hosted': {'name': 'foo', 'url': globalServer.url + '//'},
+            'hosted': {'name': 'foo', 'url': '${globalServer.url}//'},
           },
         }),
       ]).create();
@@ -81,7 +81,7 @@
     ///
     /// This is a bit of a hack, to easily test if hosted pub URLs with a path
     /// segment works and if the slashes are normalized.
-    void _proxyMyFolderToRoot() {
+    void proxyMyFolderToRoot() {
       globalServer.handle(
         RegExp('/my-folder/.*'),
         (r) async {
@@ -90,7 +90,7 @@
           }
           final path = r.requestedUri.path.substring('/my-folder/'.length);
           final res = await http.get(
-            Uri.parse(globalServer.url + '/$path'),
+            Uri.parse('${globalServer.url}/$path'),
           );
           return Response(res.statusCode, body: res.bodyBytes, headers: {
             'Content-Type': res.headers['content-type']!,
@@ -102,11 +102,11 @@
     test('will use normalized url with path', () async {
       final server = await servePackages();
       server.serve('foo', '1.2.3');
-      _proxyMyFolderToRoot();
+      proxyMyFolderToRoot();
 
       // testing with a normalized URL
-      final testUrl = globalServer.url + '/my-folder/';
-      final normalizedUrl = globalServer.url + '/my-folder/';
+      final testUrl = '${globalServer.url}/my-folder/';
+      final normalizedUrl = '${globalServer.url}/my-folder/';
 
       await d.dir(appPath, [
         d.appPubspec({
@@ -126,11 +126,11 @@
     test('will normalize url with path by adding slash', () async {
       final server = await servePackages();
       server.serve('foo', '1.2.3');
-      _proxyMyFolderToRoot();
+      proxyMyFolderToRoot();
 
       // Testing with a URL that is missing the slash.
-      final testUrl = globalServer.url + '/my-folder';
-      final normalizedUrl = globalServer.url + '/my-folder/';
+      final testUrl = '${globalServer.url}/my-folder';
+      final normalizedUrl = '${globalServer.url}/my-folder/';
 
       await d.dir(appPath, [
         d.appPubspec({
diff --git a/test/ignore_test.dart b/test/ignore_test.dart
index a8ab8ac..9d80c5b 100644
--- a/test/ignore_test.dart
+++ b/test/ignore_test.dart
@@ -20,7 +20,7 @@
   });
 
   group('pub', () {
-    void _testIgnorePath(
+    void testIgnorePath(
       TestData c,
       String path,
       bool expected,
@@ -87,10 +87,10 @@
       c.paths.forEach((path, expected) {
         var ignoreCase = c.ignoreCase;
         if (ignoreCase == null) {
-          _testIgnorePath(c, path, expected, false);
-          _testIgnorePath(c, path, expected, true);
+          testIgnorePath(c, path, expected, false);
+          testIgnorePath(c, path, expected, true);
         } else {
-          _testIgnorePath(c, path, expected, ignoreCase);
+          testIgnorePath(c, path, expected, ignoreCase);
         }
       });
     }
@@ -124,7 +124,7 @@
       runGit(['clean', '-f', '-d', '-x'], workingDirectory: tmp!.path);
     });
 
-    void _testIgnorePath(
+    void testIgnorePath(
       TestData c,
       String path,
       bool expected,
@@ -144,12 +144,12 @@
 
         for (final directory in c.patterns.keys) {
           final resolvedDirectory =
-              directory == '' ? tmp!.uri : tmp!.uri.resolve(directory + '/');
+              directory == '' ? tmp!.uri : tmp!.uri.resolve('$directory/');
           Directory.fromUri(resolvedDirectory).createSync(recursive: true);
           final gitIgnore =
               File.fromUri(resolvedDirectory.resolve('.gitignore'));
           gitIgnore.writeAsStringSync(
-            c.patterns[directory]!.join('\n') + '\n',
+            '${c.patterns[directory]!.join('\n')}\n',
           );
         }
         final process = runGit(
@@ -173,10 +173,10 @@
       c.paths.forEach((path, expected) {
         var ignoreCase = c.ignoreCase;
         if (ignoreCase == null) {
-          _testIgnorePath(c, path, expected, false);
-          _testIgnorePath(c, path, expected, true);
+          testIgnorePath(c, path, expected, false);
+          testIgnorePath(c, path, expected, true);
         } else {
-          _testIgnorePath(c, path, expected, ignoreCase);
+          testIgnorePath(c, path, expected, ignoreCase);
         }
       });
     }
diff --git a/test/lish/archives_and_uploads_a_package_test.dart b/test/lish/archives_and_uploads_a_package_test.dart
index b033e28..41db57c 100644
--- a/test/lish/archives_and_uploads_a_package_test.dart
+++ b/test/lish/archives_and_uploads_a_package_test.dart
@@ -67,7 +67,7 @@
     await d.tokensFile({
       'version': 1,
       'hosted': [
-        {'url': globalServer.url + '/sub/folder', 'env': 'TOKEN'},
+        {'url': '${globalServer.url}/sub/folder', 'env': 'TOKEN'},
       ]
     }).create();
     var pub = await startPublish(
diff --git a/test/lish/many_files_test.dart b/test/lish/many_files_test.dart
index 36f4ed7..ebb5a65 100644
--- a/test/lish/many_files_test.dart
+++ b/test/lish/many_files_test.dart
@@ -11,6 +11,7 @@
 import 'package:test/test.dart';
 
 import '../descriptor.dart' as d;
+import '../golden_file.dart';
 import '../test_pub.dart';
 import 'utils.dart';
 
@@ -27,6 +28,36 @@
 const _pathMax = 260 - 1;
 
 void main() {
+  testWithGolden('displays all files', (context) async {
+    await d.validPackage.create();
+    await d.dir(
+      appPath,
+      [
+        d.dir(
+          'lib',
+          List.generate(20, (i) => d.file('file_$i.dart')),
+        ),
+      ],
+    ).create();
+    await servePackages();
+    await d.credentialsFile(globalServer, 'access token').create();
+    var pub = await startPublish(globalServer);
+    pub.stdin.writeln('y');
+    handleUploadForm(globalServer);
+    handleUpload(globalServer);
+
+    globalServer.expect('GET', '/create', (request) {
+      return shelf.Response.ok(jsonEncode({
+        'success': {'message': 'Package test_pkg 1.0.0 uploaded!'}
+      }));
+    });
+    await pub.shouldExit(exit_codes.SUCCESS);
+    final stdout = await pub.stdout.rest.toList();
+
+    context.expectNextSection(
+        stdout.join('\n').replaceAll(globalServer.port.toString(), r'$PORT'));
+  });
+
   test(
       'archives and uploads a package with more files than can fit on '
       'the command line', () async {
diff --git a/test/must_pub_get_test.dart b/test/must_pub_get_test.dart
index dfc09d7..71d9c16 100644
--- a/test/must_pub_get_test.dart
+++ b/test/must_pub_get_test.dart
@@ -213,7 +213,7 @@
           d.appPubspec({'foo': '1.0.0'})
         ]).create();
 
-        await pubGet();
+        await pubGet(args: ['--legacy-packages-file']);
 
         deleteEntry(p.join(d.sandbox, cachePath));
 
@@ -235,7 +235,7 @@
           })
         ]).create();
 
-        await pubGet();
+        await pubGet(args: ['--legacy-packages-file']);
 
         await createPackagesFile(appPath);
 
@@ -257,7 +257,7 @@
           })
         ]).create();
 
-        await pubGet();
+        await pubGet(args: ['--legacy-packages-file']);
 
         await d.dir(appPath, [
           d.file('.packages', '''
@@ -284,7 +284,7 @@
           })
         ]).create();
 
-        await pubGet();
+        await pubGet(args: ['--legacy-packages-file']);
 
         await createPackagesFile(appPath, dependenciesInSandBox: ['foo']);
 
@@ -452,7 +452,7 @@
   group("doesn't require the user to run pub get first if", () {
     group(
         'the pubspec is older than the lockfile which is older than the '
-        'packages file, even if the contents are wrong', () {
+        'package-config, even if the contents are wrong', () {
       setUp(() async {
         await d.dir(appPath, [
           d.appPubspec({'foo': '1.0.0'})
@@ -461,7 +461,6 @@
         await _touch('pubspec.yaml');
 
         await _touch('pubspec.lock');
-        await _touch('.packages');
         await _touch('.dart_tool/package_config.json');
       });
 
@@ -600,14 +599,11 @@
           File(p.join(d.sandbox, 'myapp/pubspec.yaml')).lastModifiedSync();
       var lockFileModified =
           File(p.join(d.sandbox, 'myapp/pubspec.lock')).lastModifiedSync();
-      var packagesModified =
-          File(p.join(d.sandbox, 'myapp/.packages')).lastModifiedSync();
       var packageConfigModified =
           File(p.join(d.sandbox, 'myapp/.dart_tool/package_config.json'))
               .lastModifiedSync();
 
       expect(!pubspecModified.isAfter(lockFileModified), isTrue);
-      expect(!lockFileModified.isAfter(packagesModified), isTrue);
       expect(!lockFileModified.isAfter(packageConfigModified), isTrue);
     });
   }
diff --git a/test/package_server.dart b/test/package_server.dart
index 952c189..6aaea68 100644
--- a/test/package_server.dart
+++ b/test/package_server.dart
@@ -186,7 +186,7 @@
     contents ??= [d.libDir(name, '$name $version')];
     contents = [d.file('pubspec.yaml', yaml(pubspecFields)), ...contents];
 
-    var package = _packages.putIfAbsent(name, () => _ServedPackage());
+    var package = _packages.putIfAbsent(name, _ServedPackage.new);
     package.versions[version] = _ServedPackageVersion(
       pubspecFields,
       contents: () {
diff --git a/test/packages_file_test.dart b/test/packages_file_test.dart
index e7736be..c245a9a 100644
--- a/test/packages_file_test.dart
+++ b/test/packages_file_test.dart
@@ -11,7 +11,7 @@
 
 void main() {
   forBothPubGetAndUpgrade((command) {
-    test('.packages file is created', () async {
+    test('.packages file is created with flag', () async {
       await servePackages()
         ..serve('foo', '1.2.3',
             deps: {'baz': '2.2.2'}, contents: [d.dir('lib', [])])
@@ -24,7 +24,7 @@
         d.dir('lib')
       ]).create();
 
-      await pubCommand(command);
+      await pubCommand(command, args: ['--legacy-packages-file']);
 
       await d.dir(appPath, [
         d.packagesFile(
@@ -32,7 +32,7 @@
       ]).validate();
     });
 
-    test('.packages file is overwritten', () async {
+    test('.packages file is overwritten with flag', () async {
       await servePackages()
         ..serve('foo', '1.2.3',
             deps: {'baz': '2.2.2'}, contents: [d.dir('lib', [])])
@@ -51,7 +51,7 @@
       await oldFile.create();
       await oldFile.validate(); // Sanity-check that file was created correctly.
 
-      await pubCommand(command);
+      await pubCommand(command, args: ['--legacy-packages-file']);
 
       await d.dir(appPath, [
         d.packagesFile(
@@ -59,24 +59,28 @@
       ]).validate();
     });
 
-    test('.packages file is not created if pub command fails', () async {
+    test('.packages file is not created if pub command fails with flag',
+        () async {
       await d.dir(appPath, [
         d.appPubspec({'foo': '1.2.3'}),
         d.dir('lib')
       ]).create();
 
       await pubCommand(command,
-          args: ['--offline'], error: equalsIgnoringWhitespace("""
+          args: ['--offline', '--legacy-packages-file'],
+          error: equalsIgnoringWhitespace("""
             Because myapp depends on foo any which doesn't exist (could not find
               package foo in cache), version solving failed.
 
             Try again without --offline!
-          """), exitCode: exit_codes.UNAVAILABLE);
+          """),
+          exitCode: exit_codes.UNAVAILABLE);
 
       await d.dir(appPath, [d.nothing('.packages')]).validate();
     });
 
-    test('.packages file has relative path to path dependency', () async {
+    test('.packages file has relative path to path dependency with flag',
+        () async {
       await servePackages()
         ..serve('foo', '1.2.3',
             deps: {'baz': 'any'}, contents: [d.dir('lib', [])])
@@ -100,7 +104,7 @@
         d.dir('lib')
       ]).create();
 
-      await pubCommand(command);
+      await pubCommand(command, args: ['--legacy-packages-file']);
 
       await d.dir(appPath, [
         d.packagesFile({'myapp': '.', 'baz': '../local_baz', 'foo': '1.2.3'}),
diff --git a/test/pub_get_and_upgrade_test.dart b/test/pub_get_and_upgrade_test.dart
index 62004a4..f64f93b 100644
--- a/test/pub_get_and_upgrade_test.dart
+++ b/test/pub_get_and_upgrade_test.dart
@@ -45,7 +45,8 @@
       await pubCommand(command);
 
       await d.dir('myapp', [
-        d.packagesFile({'myapp_name': '.'})
+        d.packageConfigFile(
+            [d.packageConfigEntry(name: 'myapp_name', path: '.')]),
       ]).validate();
     });
 
diff --git a/test/pubspec_overrides_test.dart b/test/pubspec_overrides_test.dart
index 2e9234f..514d52f 100644
--- a/test/pubspec_overrides_test.dart
+++ b/test/pubspec_overrides_test.dart
@@ -2,6 +2,7 @@
 // for details. 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:pub/src/exit_codes.dart' as exit_codes;
 import 'package:test/test.dart';
 
 import 'descriptor.dart' as d;
@@ -9,7 +10,7 @@
 
 void main() {
   forBothPubGetAndUpgrade((command) {
-    test('pubspec overrides', () async {
+    test('supports dependency_overrides', () async {
       await servePackages()
         ..serve('lib', '1.0.0')
         ..serve('lib', '2.0.0');
@@ -46,4 +47,18 @@
       ]).validate();
     });
   });
+
+  test('is ignored by publish command', () async {
+    await d.validPackage.create();
+    await d.dir(appPath, [
+      d.pubspecOverrides({
+        'dependency_overrides': {'lib': '1.0.0'}
+      }),
+    ]).create();
+
+    await runPub(
+      args: ['lish', '--dry-run'],
+      exitCode: exit_codes.SUCCESS,
+    );
+  });
 }
diff --git a/test/remove/remove_test.dart b/test/remove/remove_test.dart
index e369c85..8c82527 100644
--- a/test/remove/remove_test.dart
+++ b/test/remove/remove_test.dart
@@ -21,7 +21,7 @@
     await pubRemove(args: ['foo']);
 
     await d.cacheDir({}).validate();
-    await d.appPackagesFile({}).validate();
+    await d.appPackageConfigFile([]).validate();
     await d.appDir().validate();
   });
 
@@ -49,7 +49,9 @@
     await pubRemove(args: ['foo']);
 
     await d.cacheDir({'bar': '2.0.0'}).validate();
-    await d.appPackagesFile({'bar': '2.0.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'bar', version: '2.0.0'),
+    ]).validate();
 
     await d.dir(appPath, [
       d.pubspec({
@@ -113,7 +115,7 @@
     await pubRemove(args: ['foo']);
 
     await d.cacheDir({}).validate();
-    await d.appPackagesFile({}).validate();
+    await d.appPackageConfigFile([]).validate();
 
     await d.dir(appPath, [
       d.pubspec({'name': 'myapp'})
@@ -140,7 +142,10 @@
     await pubRemove(args: ['foo', 'bar', 'baz']);
 
     await d.cacheDir({'jfj': '0.2.1'}).validate();
-    await d.appPackagesFile({'jfj': '0.2.1'}).validate();
+
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'jfj', version: '0.2.1'),
+    ]).validate();
 
     await d.dir(appPath, [
       d.pubspec({
@@ -170,7 +175,10 @@
     await pubGet();
 
     await pubRemove(args: ['foo']);
-    await d.appPackagesFile({'bar': '1.2.3'}).validate();
+
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'bar', version: '1.2.3'),
+    ]).validate();
     await d.appDir({'bar': '1.2.3'}).validate();
   });
 
@@ -188,7 +196,9 @@
     await pubGet();
 
     await pubRemove(args: ['foo']);
-    await d.appPackagesFile({'bar': '1.2.3'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'bar', version: '1.2.3'),
+    ]).validate();
     await d.appDir({'bar': '1.2.3'}).validate();
   });
 
@@ -210,7 +220,9 @@
     await pubGet();
 
     await pubRemove(args: ['foo']);
-    await d.appPackagesFile({'bar': '2.0.1'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'bar', version: '2.0.1'),
+    ]).validate();
     await d.appDir({'bar': '2.0.1'}).validate();
   });
 
diff --git a/test/sdk_test.dart b/test/sdk_test.dart
index 6e50ff0..6165cd5 100644
--- a/test/sdk_test.dart
+++ b/test/sdk_test.dart
@@ -37,13 +37,10 @@
       }).create();
       await pubCommand(command,
           environment: {'FLUTTER_ROOT': p.join(d.sandbox, 'flutter')});
-
-      await d.dir(appPath, [
-        d.packagesFile({
-          'myapp': '.',
-          'foo': p.join(d.sandbox, 'flutter', 'packages', 'foo'),
-          'bar': '1.0.0'
-        })
+      await d.appPackageConfigFile([
+        d.packageConfigEntry(
+            name: 'foo', path: p.join(d.sandbox, 'flutter', 'packages', 'foo')),
+        d.packageConfigEntry(name: 'bar', version: '1.0.0'),
       ]).validate();
     });
 
@@ -54,11 +51,10 @@
       await pubCommand(command,
           environment: {'FLUTTER_ROOT': p.join(d.sandbox, 'flutter')});
 
-      await d.dir(appPath, [
-        d.packagesFile({
-          'myapp': '.',
-          'baz': p.join(d.sandbox, 'flutter', 'bin', 'cache', 'pkg', 'baz')
-        })
+      await d.appPackageConfigFile([
+        d.packageConfigEntry(
+            name: 'baz',
+            path: p.join(d.sandbox, 'flutter', 'bin', 'cache', 'pkg', 'baz')),
       ]).validate();
     });
 
@@ -93,10 +89,7 @@
       deleteEntry(p.join(d.sandbox, 'flutter', 'version'));
       await pubCommand(command,
           environment: {'FLUTTER_ROOT': p.join(d.sandbox, 'flutter')});
-
-      await d.dir(appPath, [
-        d.packagesFile({'myapp': '.'})
-      ]).validate();
+      await d.appPackageConfigFile([]).validate();
     });
 
     group('fails if', () {
@@ -169,13 +162,10 @@
       }).create();
       await pubCommand(command,
           environment: {'FUCHSIA_DART_SDK_ROOT': p.join(d.sandbox, 'fuchsia')});
-
-      await d.dir(appPath, [
-        d.packagesFile({
-          'myapp': '.',
-          'foo': p.join(d.sandbox, 'fuchsia', 'packages', 'foo'),
-          'bar': '1.0.0'
-        })
+      await d.appPackageConfigFile([
+        d.packageConfigEntry(
+            name: 'foo', path: p.join(d.sandbox, 'fuchsia', 'packages', 'foo')),
+        d.packageConfigEntry(name: 'bar', version: '1.0.0'),
       ]).validate();
     });
   });
diff --git a/test/testdata/goldens/dependency_services/dependency_services_test/No pubspec.lock.txt b/test/testdata/goldens/dependency_services/dependency_services_test/No pubspec.lock.txt
new file mode 100644
index 0000000..ca80a34
--- /dev/null
+++ b/test/testdata/goldens/dependency_services/dependency_services_test/No pubspec.lock.txt
@@ -0,0 +1,205 @@
+# GENERATED BY: test/dependency_services/dependency_services_test.dart
+
+$ cat pubspec.yaml
+{"name":"app","dependencies":{"foo":"^1.0.0","bar":{"git":{"url":"../bar.git"}}},"environment":{"sdk":">=0.1.2 <1.0.0"}}
+$ cat pubspec.lock
+No such file pubspec.lock.
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section list
+$ dependency_services list
+{
+  "dependencies": [
+    {
+      "name": "bar",
+      "version": "92cdd4ac724a6da0db2a69ac149820aa220e8518",
+      "kind": "direct",
+      "constraint": "any",
+      "source": {
+        "type": "git",
+        "description": {
+          "url": "../bar.git",
+          "ref": "HEAD",
+          "resolved-ref": "92cdd4ac724a6da0db2a69ac149820aa220e8518",
+          "path": "."
+        }
+      }
+    },
+    {
+      "name": "foo",
+      "version": "1.2.3",
+      "kind": "direct",
+      "constraint": "^1.0.0",
+      "source": {
+        "type": "hosted",
+        "description": {
+          "name": "foo",
+          "url": "http://localhost:$PORT"
+        }
+      }
+    },
+    {
+      "name": "transitive",
+      "version": "1.0.0",
+      "kind": "transitive",
+      "constraint": "null",
+      "source": {
+        "type": "hosted",
+        "description": {
+          "name": "transitive",
+          "url": "http://localhost:$PORT"
+        }
+      }
+    }
+  ]
+}
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section report
+$ dependency_services report
+{
+  "dependencies": [
+    {
+      "name": "bar",
+      "version": "92cdd4ac724a6da0db2a69ac149820aa220e8518",
+      "kind": "direct",
+      "source": {
+        "type": "git",
+        "description": {
+          "url": "../bar.git",
+          "ref": "HEAD",
+          "resolved-ref": "92cdd4ac724a6da0db2a69ac149820aa220e8518",
+          "path": "."
+        }
+      },
+      "latest": "92cdd4ac724a6da0db2a69ac149820aa220e8518",
+      "constraint": "any",
+      "compatible": [],
+      "singleBreaking": [],
+      "multiBreaking": [
+        {
+          "name": "foo",
+          "version": "2.2.3",
+          "kind": "direct",
+          "source": {
+            "type": "hosted",
+            "description": {
+              "name": "foo",
+              "url": "http://localhost:$PORT"
+            }
+          },
+          "constraintBumped": "^2.2.3",
+          "constraintWidened": ">=1.0.0 <3.0.0",
+          "constraintBumpedIfNeeded": "^2.2.3",
+          "previousVersion": "1.2.3",
+          "previousConstraint": "^1.0.0",
+          "previousSource": {
+            "type": "hosted",
+            "description": {
+              "name": "foo",
+              "url": "http://localhost:$PORT"
+            }
+          }
+        }
+      ]
+    },
+    {
+      "name": "foo",
+      "version": "1.2.3",
+      "kind": "direct",
+      "source": {
+        "type": "hosted",
+        "description": {
+          "name": "foo",
+          "url": "http://localhost:$PORT"
+        }
+      },
+      "latest": "2.2.3",
+      "constraint": "^1.0.0",
+      "compatible": [],
+      "singleBreaking": [
+        {
+          "name": "foo",
+          "version": "2.2.3",
+          "kind": "direct",
+          "source": {
+            "type": "hosted",
+            "description": {
+              "name": "foo",
+              "url": "http://localhost:$PORT"
+            }
+          },
+          "constraintBumped": "^2.2.3",
+          "constraintWidened": ">=1.0.0 <3.0.0",
+          "constraintBumpedIfNeeded": "^2.2.3",
+          "previousVersion": "1.2.3",
+          "previousConstraint": "^1.0.0",
+          "previousSource": {
+            "type": "hosted",
+            "description": {
+              "name": "foo",
+              "url": "http://localhost:$PORT"
+            }
+          }
+        }
+      ],
+      "multiBreaking": [
+        {
+          "name": "foo",
+          "version": "2.2.3",
+          "kind": "direct",
+          "source": {
+            "type": "hosted",
+            "description": {
+              "name": "foo",
+              "url": "http://localhost:$PORT"
+            }
+          },
+          "constraintBumped": "^2.2.3",
+          "constraintWidened": ">=1.0.0 <3.0.0",
+          "constraintBumpedIfNeeded": "^2.2.3",
+          "previousVersion": "1.2.3",
+          "previousConstraint": "^1.0.0",
+          "previousSource": {
+            "type": "hosted",
+            "description": {
+              "name": "foo",
+              "url": "http://localhost:$PORT"
+            }
+          }
+        }
+      ]
+    },
+    {
+      "name": "transitive",
+      "version": "1.0.0",
+      "kind": "transitive",
+      "source": {
+        "type": "hosted",
+        "description": {
+          "name": "transitive",
+          "url": "http://localhost:$PORT"
+        }
+      },
+      "latest": "1.0.0",
+      "constraint": null,
+      "compatible": [],
+      "singleBreaking": [],
+      "multiBreaking": []
+    }
+  ]
+}
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section apply
+$ echo '{"dependencyChanges":[{"name":"foo","version":"2.2.3"},{"name":"transitive","version":null}]}' | dependency_services apply
+{"dependencies":[]}
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+$ cat pubspec.yaml
+{"name":"app","dependencies":{"foo":^2.2.3,"bar":{"git":{"url":"../bar.git"}}},"environment":{"sdk":">=0.1.2 <1.0.0"}}
+$ cat pubspec.lock
+No such file pubspec.lock.
diff --git a/test/testdata/goldens/embedding/embedding_test/logfile is written with --verbose and on unexpected exceptions.txt b/test/testdata/goldens/embedding/embedding_test/logfile is written with --verbose and on unexpected exceptions.txt
index 1d3a5f5..fc69b03 100644
--- a/test/testdata/goldens/embedding/embedding_test/logfile is written with --verbose and on unexpected exceptions.txt
+++ b/test/testdata/goldens/embedding/embedding_test/logfile is written with --verbose and on unexpected exceptions.txt
@@ -81,16 +81,6 @@
 [E]    |   version: "1.0.0"
 [E]    | sdks:
 [E]    |   dart: ">=0.1.2 <1.0.0"
-[E] IO  : Writing $N characters to text file .packages.
-[E] FINE: Contents:
-[E]    | # This file is deprecated. Tools should instead consume 
-[E]    | # `.dart_tool/package_config.json`.
-[E]    | # 
-[E]    | # For more info see: https://dart.dev/go/dot-packages-deprecation
-[E]    | # 
-[E]    | # Generated by pub on $TIME
-[E]    | foo:file://$SANDBOX/cache/hosted/localhost%2558$PORT/foo-1.0.0/lib/
-[E]    | myapp:lib/
 [E] IO  : Writing $N characters to text file .dart_tool/package_config.json.
 [E] FINE: Contents:
 [E]    | {
@@ -230,16 +220,6 @@
    | sdks:
    |   dart: ">=0.1.2 <1.0.0"
 MSG : Changed 1 dependency!
-IO  : Writing $N characters to text file .packages.
-FINE: Contents:
-   | # This file is deprecated. Tools should instead consume 
-   | # `.dart_tool/package_config.json`.
-   | # 
-   | # For more info see: https://dart.dev/go/dot-packages-deprecation
-   | # 
-   | # Generated by pub on $TIME
-   | foo:file://$SANDBOX/cache/hosted/localhost%2558$PORT/foo-1.0.0/lib/
-   | myapp:lib/
 IO  : Writing $N characters to text file .dart_tool/package_config.json.
 FINE: Contents:
    | {
diff --git a/test/testdata/goldens/help_test/pub add --help.txt b/test/testdata/goldens/help_test/pub add --help.txt
index 2a37c21..3d1f429 100644
--- a/test/testdata/goldens/help_test/pub add --help.txt
+++ b/test/testdata/goldens/help_test/pub add --help.txt
@@ -5,20 +5,22 @@
 Add dependencies to pubspec.yaml.
 
 Usage: pub add <package>[:<constraint>] [<package2>[:<constraint2>]...] [options]
--h, --help               Print this usage information.
--d, --dev                Adds to the development dependencies instead.
-    --git-url            Git URL of the package
-    --git-ref            Git branch or commit to be retrieved
-    --git-path           Path of git package in repository
-    --hosted-url         URL of package host server
-    --path               Add package from local path
-    --sdk=<[flutter]>    add package from SDK source
-                         [flutter]
-    --[no-]offline       Use cached packages instead of accessing the network.
--n, --dry-run            Report what dependencies would change but don't change
-                         any.
-    --[no-]precompile    Build executables in immediate dependencies.
--C, --directory=<dir>    Run this in the directory <dir>.
+-h, --help                    Print this usage information.
+-d, --dev                     Adds to the development dependencies instead.
+    --git-url                 Git URL of the package
+    --git-ref                 Git branch or commit to be retrieved
+    --git-path                Path of git package in repository
+    --hosted-url              URL of package host server
+    --path                    Add package from local path
+    --sdk=<[flutter]>         add package from SDK source
+                              [flutter]
+    --[no-]offline            Use cached packages instead of accessing the
+                              network.
+-n, --dry-run                 Report what dependencies would change but don't
+                              change any.
+    --[no-]precompile         Build executables in immediate dependencies.
+-C, --directory=<dir>         Run this in the directory <dir>.
+    --legacy-packages-file    Generate the legacy ".packages" file
 
 Run "pub help" to see global options.
 See https://dart.dev/tools/pub/cmd/pub-add for detailed documentation.
diff --git a/test/testdata/goldens/help_test/pub downgrade --help.txt b/test/testdata/goldens/help_test/pub downgrade --help.txt
index 4497da2..ddf71e7 100644
--- a/test/testdata/goldens/help_test/pub downgrade --help.txt
+++ b/test/testdata/goldens/help_test/pub downgrade --help.txt
@@ -7,11 +7,13 @@
 
 
 Usage: pub downgrade [dependencies...]
--h, --help               Print this usage information.
-    --[no-]offline       Use cached packages instead of accessing the network.
--n, --dry-run            Report what dependencies would change but don't change
-                         any.
--C, --directory=<dir>    Run this in the directory<dir>.
+-h, --help                    Print this usage information.
+    --[no-]offline            Use cached packages instead of accessing the
+                              network.
+-n, --dry-run                 Report what dependencies would change but don't
+                              change any.
+-C, --directory=<dir>         Run this in the directory<dir>.
+    --legacy-packages-file    Generate the legacy ".packages" file
 
 Run "pub help" to see global options.
 See https://dart.dev/tools/pub/cmd/pub-downgrade for detailed documentation.
diff --git a/test/testdata/goldens/help_test/pub get --help.txt b/test/testdata/goldens/help_test/pub get --help.txt
index 74648a2..104f69c 100644
--- a/test/testdata/goldens/help_test/pub get --help.txt
+++ b/test/testdata/goldens/help_test/pub get --help.txt
@@ -5,12 +5,14 @@
 Get the current package's dependencies.
 
 Usage: pub get <subcommand> [arguments...]
--h, --help               Print this usage information.
-    --[no-]offline       Use cached packages instead of accessing the network.
--n, --dry-run            Report what dependencies would change but don't change
-                         any.
-    --[no-]precompile    Build executables in immediate dependencies.
--C, --directory=<dir>    Run this in the directory<dir>.
+-h, --help                    Print this usage information.
+    --[no-]offline            Use cached packages instead of accessing the
+                              network.
+-n, --dry-run                 Report what dependencies would change but don't
+                              change any.
+    --[no-]precompile         Build executables in immediate dependencies.
+    --legacy-packages-file    Generate the legacy ".packages" file
+-C, --directory=<dir>         Run this in the directory<dir>.
 
 Run "pub help" to see global options.
 See https://dart.dev/tools/pub/cmd/pub-get for detailed documentation.
diff --git a/test/testdata/goldens/help_test/pub remove --help.txt b/test/testdata/goldens/help_test/pub remove --help.txt
index 7d14ed0..b82f9b6 100644
--- a/test/testdata/goldens/help_test/pub remove --help.txt
+++ b/test/testdata/goldens/help_test/pub remove --help.txt
@@ -5,12 +5,14 @@
 Removes a dependency from the current package.
 
 Usage: pub remove <package>
--h, --help               Print this usage information.
-    --[no-]offline       Use cached packages instead of accessing the network.
--n, --dry-run            Report what dependencies would change but don't change
-                         any.
-    --[no-]precompile    Precompile executables in immediate dependencies.
--C, --directory=<dir>    Run this in the directory<dir>.
+-h, --help                    Print this usage information.
+    --[no-]offline            Use cached packages instead of accessing the
+                              network.
+-n, --dry-run                 Report what dependencies would change but don't
+                              change any.
+    --[no-]precompile         Precompile executables in immediate dependencies.
+-C, --directory=<dir>         Run this in the directory<dir>.
+    --legacy-packages-file    Generate the legacy ".packages" file
 
 Run "pub help" to see global options.
 See https://dart.dev/tools/pub/cmd/pub-remove for detailed documentation.
diff --git a/test/testdata/goldens/help_test/pub upgrade --help.txt b/test/testdata/goldens/help_test/pub upgrade --help.txt
index d1a29b9..c787951 100644
--- a/test/testdata/goldens/help_test/pub upgrade --help.txt
+++ b/test/testdata/goldens/help_test/pub upgrade --help.txt
@@ -5,16 +5,18 @@
 Upgrade the current package's dependencies to latest versions.
 
 Usage: pub upgrade [dependencies...]
--h, --help               Print this usage information.
-    --[no-]offline       Use cached packages instead of accessing the network.
--n, --dry-run            Report what dependencies would change but don't change
-                         any.
-    --[no-]precompile    Precompile executables in immediate dependencies.
-    --null-safety        Upgrade constraints in pubspec.yaml to null-safety
-                         versions
-    --major-versions     Upgrades packages to their latest resolvable versions,
-                         and updates pubspec.yaml.
--C, --directory=<dir>    Run this in the directory<dir>.
+-h, --help                    Print this usage information.
+    --[no-]offline            Use cached packages instead of accessing the
+                              network.
+-n, --dry-run                 Report what dependencies would change but don't
+                              change any.
+    --[no-]precompile         Precompile executables in immediate dependencies.
+    --null-safety             Upgrade constraints in pubspec.yaml to null-safety
+                              versions
+    --legacy-packages-file    Generate the legacy ".packages" file
+    --major-versions          Upgrades packages to their latest resolvable
+                              versions, and updates pubspec.yaml.
+-C, --directory=<dir>         Run this in the directory<dir>.
 
 Run "pub help" to see global options.
 See https://dart.dev/tools/pub/cmd/pub-upgrade for detailed documentation.
diff --git a/test/testdata/goldens/lish/many_files_test/displays all files.txt b/test/testdata/goldens/lish/many_files_test/displays all files.txt
new file mode 100644
index 0000000..09a01da
--- /dev/null
+++ b/test/testdata/goldens/lish/many_files_test/displays all files.txt
@@ -0,0 +1,36 @@
+# GENERATED BY: test/lish/many_files_test.dart
+
+Publishing test_pkg 1.0.0 to http://localhost:$PORT:
+|-- CHANGELOG.md
+|-- LICENSE
+|-- README.md
+|-- lib
+|   |-- file_0.dart
+|   |-- file_1.dart
+|   |-- file_10.dart
+|   |-- file_11.dart
+|   |-- file_12.dart
+|   |-- file_13.dart
+|   |-- file_14.dart
+|   |-- file_15.dart
+|   |-- file_16.dart
+|   |-- file_17.dart
+|   |-- file_18.dart
+|   |-- file_19.dart
+|   |-- file_2.dart
+|   |-- file_3.dart
+|   |-- file_4.dart
+|   |-- file_5.dart
+|   |-- file_6.dart
+|   |-- file_7.dart
+|   |-- file_8.dart
+|   |-- file_9.dart
+|   '-- test_pkg.dart
+'-- pubspec.yaml
+
+Publishing is forever; packages cannot be unpublished.
+Policy details are available at https://pub.dev/policy
+
+Do you want to publish test_pkg 1.0.0 to http://localhost:$PORT (y/N)?
+Uploading...
+Package test_pkg 1.0.0 uploaded!
\ No newline at end of file
diff --git a/test/unknown_source_test.dart b/test/unknown_source_test.dart
index fc53621..37591d0 100644
--- a/test/unknown_source_test.dart
+++ b/test/unknown_source_test.dart
@@ -72,7 +72,9 @@
       await pubCommand(command);
 
       // Should upgrade to the new one.
-      await d.appPackagesFile({'foo': '../foo'}).validate();
+      await d.appPackageConfigFile([
+        d.packageConfigEntry(name: 'foo', path: '../foo'),
+      ]).validate();
     });
   });
 }
diff --git a/test/upgrade/hosted/unlock_if_necessary_test.dart b/test/upgrade/hosted/unlock_if_necessary_test.dart
index c285e66..2707100 100644
--- a/test/upgrade/hosted/unlock_if_necessary_test.dart
+++ b/test/upgrade/hosted/unlock_if_necessary_test.dart
@@ -20,13 +20,19 @@
 
     await pubGet();
 
-    await d.appPackagesFile({'foo': '1.0.0', 'foo_dep': '1.0.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.0.0'),
+      d.packageConfigEntry(name: 'foo_dep', version: '1.0.0'),
+    ]).validate();
 
     server.serve('foo', '2.0.0', deps: {'foo_dep': '>1.0.0'});
     server.serve('foo_dep', '2.0.0');
 
     await pubUpgrade(args: ['foo']);
 
-    await d.appPackagesFile({'foo': '2.0.0', 'foo_dep': '2.0.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '2.0.0'),
+      d.packageConfigEntry(name: 'foo_dep', version: '2.0.0'),
+    ]).validate();
   });
 }
diff --git a/test/upgrade/hosted/unlock_single_package_test.dart b/test/upgrade/hosted/unlock_single_package_test.dart
index bc78a3d..e443a6a 100644
--- a/test/upgrade/hosted/unlock_single_package_test.dart
+++ b/test/upgrade/hosted/unlock_single_package_test.dart
@@ -18,7 +18,10 @@
 
     await pubGet();
 
-    await d.appPackagesFile({'foo': '1.0.0', 'bar': '1.0.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.0.0'),
+      d.packageConfigEntry(name: 'bar', version: '1.0.0'),
+    ]).validate();
 
     server.serve('foo', '2.0.0', deps: {'bar': '<3.0.0'});
     server.serve('bar', '2.0.0');
@@ -26,16 +29,23 @@
     // This can't upgrade 'bar'
     await pubUpgrade(args: ['bar']);
 
-    await d.appPackagesFile({'foo': '1.0.0', 'bar': '1.0.0'}).validate();
-
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.0.0'),
+      d.packageConfigEntry(name: 'bar', version: '1.0.0'),
+    ]).validate();
     // Introducing foo and bar 1.1.0, to show that only 'bar' will be upgraded
     server.serve('foo', '1.1.0', deps: {'bar': '<2.0.0'});
     server.serve('bar', '1.1.0');
 
     await pubUpgrade(args: ['bar']);
-    await d.appPackagesFile({'foo': '1.0.0', 'bar': '1.1.0'}).validate();
-
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.0.0'),
+      d.packageConfigEntry(name: 'bar', version: '1.1.0'),
+    ]).validate();
     await pubUpgrade();
-    await d.appPackagesFile({'foo': '2.0.0', 'bar': '2.0.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '2.0.0'),
+      d.packageConfigEntry(name: 'bar', version: '2.0.0'),
+    ]).validate();
   });
 }
diff --git a/test/upgrade/hosted/upgrade_removed_constraints_test.dart b/test/upgrade/hosted/upgrade_removed_constraints_test.dart
index 248ac90..cf72c07 100644
--- a/test/upgrade/hosted/upgrade_removed_constraints_test.dart
+++ b/test/upgrade/hosted/upgrade_removed_constraints_test.dart
@@ -19,13 +19,19 @@
 
     await pubUpgrade();
 
-    await d.appPackagesFile(
-        {'foo': '1.0.0', 'bar': '1.0.0', 'shared_dep': '1.0.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.0.0'),
+      d.packageConfigEntry(name: 'bar', version: '1.0.0'),
+      d.packageConfigEntry(name: 'shared_dep', version: '1.0.0'),
+    ]).validate();
 
     await d.appDir({'foo': 'any'}).create();
 
     await pubUpgrade();
 
-    await d.appPackagesFile({'foo': '1.0.0', 'shared_dep': '2.0.0'}).validate();
+    await d.appPackageConfigFile([
+      d.packageConfigEntry(name: 'foo', version: '1.0.0'),
+      d.packageConfigEntry(name: 'shared_dep', version: '2.0.0'),
+    ]).validate();
   });
 }
diff --git a/test/upgrade/upgrade_major_versions_test.dart b/test/upgrade/upgrade_major_versions_test.dart
index 932ca17..c5f0994 100644
--- a/test/upgrade/upgrade_major_versions_test.dart
+++ b/test/upgrade/upgrade_major_versions_test.dart
@@ -41,12 +41,11 @@
         'bar': '^0.2.0',
         'baz': '^1.0.0',
       }).validate();
-
-      await d.appPackagesFile({
-        'foo': '2.0.0',
-        'bar': '0.2.0',
-        'baz': '1.0.1',
-      }).validate();
+      await d.appPackageConfigFile([
+        d.packageConfigEntry(name: 'foo', version: '2.0.0'),
+        d.packageConfigEntry(name: 'bar', version: '0.2.0'),
+        d.packageConfigEntry(name: 'baz', version: '1.0.1'),
+      ]).validate();
     });
 
     test('bumps dev_dependency constraints and shows summary report', () async {
@@ -92,11 +91,11 @@
         }),
       ]).validate();
 
-      await d.appPackagesFile({
-        'foo': '2.0.0',
-        'bar': '0.2.0',
-        'baz': '1.0.1',
-      }).validate();
+      await d.appPackageConfigFile([
+        d.packageConfigEntry(name: 'foo', version: '2.0.0'),
+        d.packageConfigEntry(name: 'bar', version: '0.2.0'),
+        d.packageConfigEntry(name: 'baz', version: '1.0.1'),
+      ]).validate();
     });
 
     test('upgrades only the selected package', () async {
@@ -128,7 +127,10 @@
         'bar': '^0.1.0',
       }).validate();
 
-      await d.appPackagesFile({'foo': '2.0.0', 'bar': '0.1.0'}).validate();
+      await d.appPackageConfigFile([
+        d.packageConfigEntry(name: 'foo', version: '2.0.0'),
+        d.packageConfigEntry(name: 'bar', version: '0.1.0'),
+      ]).validate();
     });
 
     test('chooses the latest version where possible', () async {
@@ -159,7 +161,9 @@
         d.file('pubspec.lock', contains('3.0.0'))
       ]).validate();
 
-      await d.appPackagesFile({'foo': '3.0.0'}).validate();
+      await d.appPackageConfigFile([
+        d.packageConfigEntry(name: 'foo', version: '3.0.0'),
+      ]).validate();
     });
 
     test('overridden dependencies - no resolution', () async {
@@ -211,7 +215,10 @@
         })
       ]).validate();
 
-      await d.appPackagesFile({'foo': '1.0.0', 'bar': '1.0.0'}).validate();
+      await d.appPackageConfigFile([
+        d.packageConfigEntry(name: 'foo', version: '1.0.0'),
+        d.packageConfigEntry(name: 'bar', version: '1.0.0'),
+      ]).validate();
     });
 
     test('upgrade should not downgrade any versions', () async {
@@ -249,10 +256,10 @@
         'bar': '^4.0.0',
       }).validate();
 
-      await d.appPackagesFile({
-        'foo': '1.0.0',
-        'bar': '4.0.0',
-      }).validate();
+      await d.appPackageConfigFile([
+        d.packageConfigEntry(name: 'foo', version: '1.0.0'),
+        d.packageConfigEntry(name: 'bar', version: '4.0.0'),
+      ]).validate();
     });
   });
 }
diff --git a/test/utils_test.dart b/test/utils_test.dart
index c416951..78ccbaf 100644
--- a/test/utils_test.dart
+++ b/test/utils_test.dart
@@ -128,7 +128,7 @@
     test('is stable', () async {
       {
         final completers = <String, Completer>{};
-        Completer completer(k) => completers.putIfAbsent(k, () => Completer());
+        Completer completer(k) => completers.putIfAbsent(k, Completer.new);
         Future<int> lengthWhenComplete(String s) async {
           await completer(s).future;
           return s.length;
@@ -145,7 +145,7 @@
       }
       {
         final completers = <String, Completer>{};
-        Completer completer(k) => completers.putIfAbsent(k, () => Completer());
+        Completer completer(k) => completers.putIfAbsent(k, Completer.new);
         Future<int> lengthWhenComplete(String s) async {
           await completer(s).future;
           return s.length;
diff --git a/test/validator/pubspec_test.dart b/test/validator/pubspec_test.dart
index ea0f5af..c7e1272 100644
--- a/test/validator/pubspec_test.dart
+++ b/test/validator/pubspec_test.dart
@@ -13,7 +13,7 @@
   test('should consider a package valid if it has a pubspec', () async {
     await d.validPackage.create();
 
-    await expectValidation(() => PubspecValidator());
+    await expectValidation(PubspecValidator.new);
   });
 
   test('should consider a package invalid if it has a .gitignored pubspec',
@@ -22,6 +22,6 @@
     await d.validPackage.create();
     await repo.create();
 
-    await expectValidation(() => PubspecValidator(), errors: isNotEmpty);
+    await expectValidation(PubspecValidator.new, errors: isNotEmpty);
   });
 }
diff --git a/test/validator/size_test.dart b/test/validator/size_test.dart
index ba5151d..0773bc1 100644
--- a/test/validator/size_test.dart
+++ b/test/validator/size_test.dart
@@ -13,7 +13,7 @@
 
 Future<void> expectSizeValidationError(Matcher matcher) async {
   await expectValidation(
-    () => SizeValidator(),
+    SizeValidator.new,
     size: 100 * (1 << 20) + 1,
     errors: contains(matcher),
   );
@@ -23,8 +23,8 @@
   test('considers a package valid if it is <= 100 MB', () async {
     await d.validPackage.create();
 
-    await expectValidation(() => SizeValidator(), size: 100);
-    await expectValidation(() => SizeValidator(), size: 100 * (1 << 20));
+    await expectValidation(SizeValidator.new, size: 100);
+    await expectValidation(SizeValidator.new, size: 100 * (1 << 20));
   });
 
   group('considers a package invalid if it is more than 100 MB', () {
diff --git a/tool/extract_all_pub_dev.dart b/tool/extract_all_pub_dev.dart
index f96dc5a..323c05c 100644
--- a/tool/extract_all_pub_dev.dart
+++ b/tool/extract_all_pub_dev.dart
@@ -84,7 +84,7 @@
                     .send(http.Request('GET', Uri.parse(archiveUrl)));
                 await extractTarGz(response.stream, tempDir);
                 print('Extracted $archiveUrl');
-              } catch (e, _) {
+              } catch (e) {
                 print('Failed to get and extract $archiveUrl $e');
                 failures.add({'archive': archiveUrl, 'error': e.toString()});
                 allVersionsGood = false;
diff --git a/tool/test-bin/pub_command_runner.dart b/tool/test-bin/pub_command_runner.dart
index e438aae..3c1bef9 100644
--- a/tool/test-bin/pub_command_runner.dart
+++ b/tool/test-bin/pub_command_runner.dart
@@ -14,7 +14,7 @@
 import 'package:pub/src/log.dart' as log;
 import 'package:usage/usage.dart';
 
-final _LoggingAnalytics loggingAnalytics = _LoggingAnalytics();
+final Analytics loggingAnalytics = _LoggingAnalytics();
 
 // A command for explicitly throwing an exception, to test the handling of
 // unexpected eceptions.