diff --git a/script/tool/lib/src/publish_command.dart b/script/tool/lib/src/publish_command.dart index e7b3d110c5fab..717fb3b53733c 100644 --- a/script/tool/lib/src/publish_command.dart +++ b/script/tool/lib/src/publish_command.dart @@ -99,6 +99,11 @@ class PublishCommand extends PackageLoopingCommand { // `flutter_plugin_tools-v0.0.24`. static const String _tagFormat = '%PACKAGE%-v%VERSION%'; + /// Returns the correct path where the pub credential is stored. + @visibleForTesting + late final String credentialsPath = + _getCredentialsPath(platform: platform, path: path); + @override final String name = 'publish'; @@ -397,13 +402,12 @@ Safe to ignore if the package is deleted in this commit. } void _ensureValidPubCredential() { - final String credentialsPath = _credentialsPath; final File credentialFile = packagesDir.fileSystem.file(credentialsPath); if (credentialFile.existsSync() && credentialFile.readAsStringSync().isNotEmpty) { return; } - final String? credential = io.Platform.environment[_pubCredentialName]; + final String? credential = platform.environment[_pubCredentialName]; if (credential == null) { printError(''' No pub credential available. Please check if `$credentialsPath` is valid. @@ -415,42 +419,46 @@ If running this command on CI, you can set the pub credential content in the $_p ..writeStringSync(credential) ..closeSync(); } - - /// Returns the correct path where the pub credential is stored. - @visibleForTesting - static String getCredentialPath() { - return _credentialsPath; - } } /// The path in which pub expects to find its credentials file. -final String _credentialsPath = () { - // This follows the same logic as pub: - // https://github.com/dart-lang/pub/blob/d99b0d58f4059d7bb4ac4616fd3d54ec00a2b5d4/lib/src/system_cache.dart#L34-L43 - String? cacheDir; - final String? pubCache = io.Platform.environment['PUB_CACHE']; - if (pubCache != null) { - cacheDir = pubCache; - } else if (io.Platform.isWindows) { - final String? appData = io.Platform.environment['APPDATA']; +String _getCredentialsPath( + {required Platform platform, required p.Context path}) { + // See https://github.com/dart-lang/pub/blob/master/doc/cache_layout.md#layout + String? configDir; + if (platform.isLinux) { + String? configHome = platform.environment['XDG_CONFIG_HOME']; + if (configHome == null) { + final String? home = platform.environment['HOME']; + if (home == null) { + printError('"HOME" environment variable is not set.'); + } else { + configHome = path.join(home, '.config'); + } + } + if (configHome != null) { + configDir = path.join(configHome, 'dart'); + } + } else if (platform.isWindows) { + final String? appData = platform.environment['APPDATA']; if (appData == null) { printError('"APPDATA" environment variable is not set.'); } else { - cacheDir = p.join(appData, 'Pub', 'Cache'); + configDir = path.join(appData, 'dart'); } - } else { - final String? home = io.Platform.environment['HOME']; + } else if (platform.isMacOS) { + final String? home = platform.environment['HOME']; if (home == null) { printError('"HOME" environment variable is not set.'); } else { - cacheDir = p.join(home, '.pub-cache'); + configDir = path.join(home, 'Library', 'Application Support', 'dart'); } } - if (cacheDir == null) { - printError('Unable to determine pub cache location'); + if (configDir == null) { + printError('Unable to determine pub con location'); throw ToolExit(1); } - return p.join(cacheDir, 'credentials.json'); -}(); + return path.join(configDir, 'pub-credentials.json'); +} diff --git a/script/tool/test/publish_command_test.dart b/script/tool/test/publish_command_test.dart index 94023032ecb66..bbee7f94a5301 100644 --- a/script/tool/test/publish_command_test.dart +++ b/script/tool/test/publish_command_test.dart @@ -24,9 +24,11 @@ import 'util.dart'; void main() { final String flutterCommand = getFlutterCommand(const LocalPlatform()); + late MockPlatform platform; late Directory packagesDir; late MockGitDir gitDir; late TestProcessRunner processRunner; + late PublishCommand command; late CommandRunner commandRunner; late MockStdin mockStdin; late FileSystem fileSystem; @@ -34,13 +36,14 @@ void main() { late Map> mockHttpResponses; void createMockCredentialFile() { - final String credentialPath = PublishCommand.getCredentialPath(); - fileSystem.file(credentialPath) + fileSystem.file(command.credentialsPath) ..createSync(recursive: true) ..writeAsStringSync('some credential'); } setUp(() async { + platform = MockPlatform(isLinux: true); + platform.environment['HOME'] = '/home'; fileSystem = MemoryFileSystem(); packagesDir = createPackagesDirectory(fileSystem: fileSystem); processRunner = TestProcessRunner(); @@ -71,14 +74,15 @@ void main() { }); mockStdin = MockStdin(); - commandRunner = CommandRunner('tester', '') - ..addCommand(PublishCommand( - packagesDir, - processRunner: processRunner, - stdinput: mockStdin, - gitDir: gitDir, - httpClient: mockClient, - )); + command = PublishCommand( + packagesDir, + platform: platform, + processRunner: processRunner, + stdinput: mockStdin, + gitDir: gitDir, + httpClient: mockClient, + ); + commandRunner = CommandRunner('tester', '')..addCommand(command); }); group('Initial validation', () { @@ -880,6 +884,44 @@ void main() { isNot(contains('git-push'))); }); }); + + group('credential location', () { + test('Linux with XDG', () async { + platform = MockPlatform(isLinux: true); + platform.environment['XDG_CONFIG_HOME'] = '/xdghome/config'; + command = PublishCommand(packagesDir, platform: platform); + + expect( + command.credentialsPath, '/xdghome/config/dart/pub-credentials.json'); + }); + + test('Linux without XDG', () async { + platform = MockPlatform(isLinux: true); + platform.environment['HOME'] = '/home'; + command = PublishCommand(packagesDir, platform: platform); + + expect( + command.credentialsPath, '/home/.config/dart/pub-credentials.json'); + }); + + test('macOS', () async { + platform = MockPlatform(isMacOS: true); + platform.environment['HOME'] = '/Users/someuser'; + command = PublishCommand(packagesDir, platform: platform); + + expect(command.credentialsPath, + '/Users/someuser/Library/Application Support/dart/pub-credentials.json'); + }); + + test('Windows', () async { + platform = MockPlatform(isWindows: true); + platform.environment['APPDATA'] = r'C:\Users\SomeUser\AppData'; + command = PublishCommand(packagesDir, platform: platform); + + expect(command.credentialsPath, + r'C:\Users\SomeUser\AppData\dart\pub-credentials.json'); + }); + }); } /// An extension of [RecordingProcessRunner] that stores 'flutter pub publish'