Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[tool] Use new pub cache location for publish #3962

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 33 additions & 25 deletions script/tool/lib/src/publish_command.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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.
Expand All @@ -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');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: You've changed p -> path in multiple places, but the import is still as p.

Copy link
Contributor Author

@stuartmorgan stuartmorgan May 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's actually correct; path is a p.Context instance (from our base command class) constructed based on the platform instance, which means it follows the mock platform if one is injected by tests.

What makes this extra confusing I think is that the path package provides everything as both methods on a context, and free functions (which call the method of the same name on the default context). So calling the free functions with a prefixed import and calling the methods on an instance look the same, even though they are actually calling different things.

}
62 changes: 52 additions & 10 deletions script/tool/test/publish_command_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,26 @@ 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<void> commandRunner;
late MockStdin mockStdin;
late FileSystem fileSystem;
// Map of package name to mock response.
late Map<String, Map<String, dynamic>> 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();
Expand Down Expand Up @@ -71,14 +74,15 @@ void main() {
});

mockStdin = MockStdin();
commandRunner = CommandRunner<void>('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<void>('tester', '')..addCommand(command);
});

group('Initial validation', () {
Expand Down Expand Up @@ -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'
Expand Down