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,
+ );
+ });
}