Use applicationConfigHome from package:cli_util (#3164) Also handle missing config-dir: * When trying to read a file from the config-dir we just ignore it (no file existed). * When trying to write we throw a DataException.
diff --git a/lib/src/authentication/token_store.dart b/lib/src/authentication/token_store.dart index 24f70fc..8431088 100644 --- a/lib/src/authentication/token_store.dart +++ b/lib/src/authentication/token_store.dart
@@ -5,9 +5,12 @@ // @dart=2.11 import 'dart:convert'; +import 'dart:io'; +import 'package:meta/meta.dart'; import 'package:path/path.dart' as path; +import '../exceptions.dart'; import '../io.dart'; import '../log.dart' as log; import 'credential.dart'; @@ -28,9 +31,9 @@ /// Reads "pub-tokens.json" and parses / deserializes it into list of /// [Credential]. List<Credential> _loadCredentials() { - final result = List<Credential>.empty(growable: true); + final result = <Credential>[]; final path = _tokensFile; - if (!fileExists(path)) { + if (path == null || !fileExists(path)) { return result; } @@ -90,11 +93,21 @@ return result; } + @alwaysThrows + void missingConfigDir() { + final variable = Platform.isWindows ? '%APPDATA%' : r'$HOME'; + throw DataException('No config dir found. Check that $variable is set'); + } + /// Writes [credentials] into "pub-tokens.json". void _saveCredentials(List<Credential> credentials) { - ensureDir(path.dirname(_tokensFile)); + final tokensFile = _tokensFile; + if (tokensFile == null) { + missingConfigDir(); + } + ensureDir(path.dirname(tokensFile)); writeTextFile( - _tokensFile, + tokensFile, jsonEncode(<String, dynamic>{ 'version': 1, 'hosted': credentials.map((it) => it.toJson()).toList(), @@ -161,10 +174,18 @@ /// Deletes pub-tokens.json file from the disk. void deleteTokensFile() { - deleteEntry(_tokensFile); - log.message('pub-tokens.json is deleted.'); + final tokensFile = _tokensFile; + if (tokensFile == null) { + missingConfigDir(); + } else if (!fileExists(tokensFile)) { + log.message('No credentials file found at "$tokensFile"'); + } else { + deleteEntry(_tokensFile); + log.message('pub-tokens.json is deleted.'); + } } /// Full path to the "pub-tokens.json" file. - String get _tokensFile => path.join(configDir, 'pub-tokens.json'); + String get _tokensFile => + configDir == null ? null : path.join(configDir, 'pub-tokens.json'); }
diff --git a/lib/src/io.dart b/lib/src/io.dart index f95408d..b4898ff 100644 --- a/lib/src/io.dart +++ b/lib/src/io.dart
@@ -8,6 +8,8 @@ import 'dart:convert'; import 'dart:io'; +import 'package:cli_util/cli_util.dart' + show EnvironmentNotFoundException, applicationConfigHome; import 'package:http/http.dart' show ByteStream; import 'package:http_multi_server/http_multi_server.dart'; import 'package:meta/meta.dart'; @@ -1025,22 +1027,16 @@ } /// The location for dart-specific configuration. -final String dartConfigDir = () { - // TODO: Migrate to new value from cli_util - if (runningFromTest) { +/// +/// `null` if no config dir could be found. +final String? dartConfigDir = () { + if (runningFromTest && + Platform.environment.containsKey('_PUB_TEST_CONFIG_DIR')) { return Platform.environment['_PUB_TEST_CONFIG_DIR']; } - String configDir; - if (Platform.isLinux) { - configDir = Platform.environment['XDG_CONFIG_HOME'] ?? - path.join(Platform.environment['HOME']!, '.config'); - } else if (Platform.isWindows) { - configDir = Platform.environment['APPDATA']!; - } else if (Platform.isMacOS) { - configDir = path.join( - Platform.environment['HOME']!, 'Library', 'Application Support'); - } else { - configDir = path.join(Platform.environment['HOME']!, '.config'); + try { + return applicationConfigHome('dart'); + } on EnvironmentNotFoundException { + return null; } - return path.join(configDir, 'dart'); -}()!; +}();
diff --git a/lib/src/oauth2.dart b/lib/src/oauth2.dart index c1284e9..20d32c1 100644 --- a/lib/src/oauth2.dart +++ b/lib/src/oauth2.dart
@@ -191,10 +191,17 @@ /// best place for storing secrets, as it might be shared. /// /// To provide backwards compatibility we use the legacy file if only it exists. +/// +/// Returns `null` if there is no good place for the file. String _credentialsFile(SystemCache cache) { - final newCredentialsFile = path.join(dartConfigDir, 'pub-credentials.json'); - return [newCredentialsFile, _legacyCredentialsFile(cache)] - .firstWhere(fileExists, orElse: () => newCredentialsFile); + final configDir = dartConfigDir; + + final newCredentialsFile = + configDir == null ? null : path.join(configDir, 'pub-credentials.json'); + return [ + if (newCredentialsFile != null) newCredentialsFile, + _legacyCredentialsFile(cache) + ].firstWhere(fileExists, orElse: () => newCredentialsFile); } String _legacyCredentialsFile(SystemCache cache) {
diff --git a/pubspec.yaml b/pubspec.yaml index 23fc0c8..d3e643b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml
@@ -9,7 +9,7 @@ analyzer: ^1.5.0 args: ^2.1.0 async: ^2.6.1 - cli_util: ^0.3.0 + cli_util: ^0.3.5 collection: ^1.15.0 crypto: ^3.0.1 frontend_server_client: ^2.0.0
diff --git a/test/test_pub.dart b/test/test_pub.dart index 356b959..b7bb581 100644 --- a/test/test_pub.dart +++ b/test/test_pub.dart
@@ -138,6 +138,7 @@ int exitCode, Map<String, String> environment, String workingDirectory, + includeParentEnvironment = true, }) async { if (error != null && warning != null) { throw ArgumentError("Cannot pass both 'error' and 'warning'."); @@ -161,7 +162,8 @@ silent: silent, exitCode: exitCode, environment: environment, - workingDirectory: workingDirectory); + workingDirectory: workingDirectory, + includeParentEnvironment: includeParentEnvironment); } Future<void> pubAdd({ @@ -192,6 +194,7 @@ int exitCode, Map<String, String> environment, String workingDirectory, + bool includeParentEnvironment = true, }) async => await pubCommand( RunCommand.get, @@ -202,6 +205,7 @@ exitCode: exitCode, environment: environment, workingDirectory: workingDirectory, + includeParentEnvironment: includeParentEnvironment, ); Future<void> pubUpgrade( @@ -330,23 +334,27 @@ /// /// If [environment] is given, any keys in it will override the environment /// variables passed to the spawned process. -Future<void> runPub({ - List<String> args, - output, - error, - outputJson, - silent, - int exitCode, - String workingDirectory, - Map<String, String> environment, - List<String> input, -}) async { +Future<void> runPub( + {List<String> args, + output, + error, + outputJson, + silent, + int exitCode, + String workingDirectory, + Map<String, String> environment, + List<String> input, + includeParentEnvironment = true}) async { exitCode ??= exit_codes.SUCCESS; // Cannot pass both output and outputJson. assert(output == null || outputJson == null); var pub = await startPub( - args: args, workingDirectory: workingDirectory, environment: environment); + args: args, + workingDirectory: workingDirectory, + environment: environment, + includeParentEnvironment: includeParentEnvironment, + ); if (input != null) { input.forEach(pub.stdin.writeln); @@ -460,21 +468,12 @@ String tokenEndpoint, String workingDirectory, Map<String, String> environment, - bool verbose = true}) async { + bool verbose = true, + includeParentEnvironment = true}) async { args ??= []; ensureDir(_pathInSandbox(appPath)); - // Find a Dart executable we can use to spawn. Use the same one that was - // used to run this script itself. - var dartBin = Platform.executable; - - // If the executable looks like a path, get its full path. That way we - // can still find it when we spawn it with a different working directory. - if (dartBin.contains(Platform.pathSeparator)) { - dartBin = p.absolute(dartBin); - } - // If there's a snapshot for "pub" available we use it. If the snapshot is // out-of-date local source the tests will be useless, therefore it is // recommended to use a temporary file with a unique name for each test run. @@ -492,11 +491,20 @@ ..addAll([pubPath, if (verbose) '--verbose']) ..addAll(args); - return await PubProcess.start(dartBin, dartArgs, - environment: getPubTestEnvironment(tokenEndpoint) - ..addAll(environment ?? {}), + final mergedEnvironment = getPubTestEnvironment(tokenEndpoint); + for (final e in (environment ?? {}).entries) { + if (e.value == null) { + mergedEnvironment.remove(e.key); + } else { + mergedEnvironment[e.key] = e.value; + } + } + + return await PubProcess.start(Platform.resolvedExecutable, dartArgs, + environment: mergedEnvironment, workingDirectory: workingDirectory ?? _pathInSandbox(appPath), - description: args.isEmpty ? 'pub' : 'pub ${args.first}'); + description: args.isEmpty ? 'pub' : 'pub ${args.first}', + includeParentEnvironment: includeParentEnvironment); } /// A subclass of [TestProcess] that parses pub's verbose logging output and
diff --git a/test/token/add_token_test.dart b/test/token/add_token_test.dart index edce8fd..5e6b1f7 100644 --- a/test/token/add_token_test.dart +++ b/test/token/add_token_test.dart
@@ -141,4 +141,15 @@ await d.dir(configPath, [d.nothing('pub-tokens.json')]).validate(); }); + + test('with empty environment gives error message', () async { + await runPub( + args: ['token', 'add', 'https://mypub.com'], + input: ['auth-token'], + error: contains('No config dir found.'), + exitCode: exit_codes.DATA, + environment: {'_PUB_TEST_CONFIG_DIR': null}, + includeParentEnvironment: false, + ); + }); }
diff --git a/test/token/remove_token_test.dart b/test/token/remove_token_test.dart index f9ce67d..24fefa2 100644 --- a/test/token/remove_token_test.dart +++ b/test/token/remove_token_test.dart
@@ -61,4 +61,14 @@ await d.dir(configPath, [d.nothing('pub-tokens.json')]).validate(); }); + + test('with empty environment gives error message', () async { + await runPub( + args: ['token', 'remove', 'http://mypub.com'], + error: contains('No config dir found.'), + exitCode: exit_codes.DATA, + environment: {'_PUB_TEST_CONFIG_DIR': null}, + includeParentEnvironment: false, + ); + }); }