Default new project to the ios-signing-cert development team (#90021)

This commit is contained in:
Jenn Magder 2021-10-07 10:23:02 -07:00 committed by GitHub
parent 4ef6fc1833
commit 91d1e3ed8b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 328 additions and 77 deletions

View file

@ -16,6 +16,7 @@ import '../features.dart';
import '../flutter_manifest.dart';
import '../flutter_project_metadata.dart';
import '../globals_null_migrated.dart' as globals;
import '../ios/code_signing.dart';
import '../project.dart';
import '../reporting/reporting.dart';
import '../runner/flutter_command.dart';
@ -234,6 +235,17 @@ class CreateCommand extends CreateBase {
}
final String dartSdk = globals.cache.dartSdkBuild;
final bool includeIos = featureFlags.isIOSEnabled && platforms.contains('ios');
String developmentTeam;
if (includeIos) {
developmentTeam = await getCodeSigningIdentityDevelopmentTeam(
processManager: globals.processManager,
platform: globals.platform,
logger: globals.logger,
config: globals.config,
terminal: globals.terminal,
);
}
final Map<String, Object> templateContext = createTemplateContext(
organization: organization,
@ -243,7 +255,8 @@ class CreateCommand extends CreateBase {
withPluginHook: generatePlugin,
androidLanguage: stringArg('android-language'),
iosLanguage: stringArg('ios-language'),
ios: featureFlags.isIOSEnabled && platforms.contains('ios'),
iosDevelopmentTeam: developmentTeam,
ios: includeIos,
android: featureFlags.isAndroidEnabled && platforms.contains('android'),
web: featureFlags.isWebEnabled && platforms.contains('web'),
linux: featureFlags.isLinuxEnabled && platforms.contains('linux'),

View file

@ -325,6 +325,7 @@ abstract class CreateBase extends FlutterCommand {
String projectName,
String projectDescription,
String androidLanguage,
String iosDevelopmentTeam,
String iosLanguage,
String flutterRoot,
String dartSdkVersionBounds,
@ -375,6 +376,8 @@ abstract class CreateBase extends FlutterCommand {
'withPluginHook': withPluginHook,
'androidLanguage': androidLanguage,
'iosLanguage': iosLanguage,
'hasIosDevelopmentTeam': iosDevelopmentTeam != null && iosDevelopmentTeam.isNotEmpty,
'iosDevelopmentTeam': iosDevelopmentTeam ?? '',
'flutterRevision': globals.flutterVersion.frameworkRevision,
'flutterChannel': globals.flutterVersion.channel,
'ios': ios,

View file

@ -8,10 +8,13 @@ import '../base/common.dart';
import '../base/config.dart';
import '../base/io.dart';
import '../base/logger.dart';
import '../base/platform.dart';
import '../base/process.dart';
import '../base/terminal.dart';
import '../convert.dart' show utf8;
const String _developmentTeamBuildSettingName = 'DEVELOPMENT_TEAM';
/// User message when no development certificates are found in the keychain.
///
/// The user likely never did any iOS development.
@ -91,9 +94,10 @@ final RegExp _certificateOrganizationalUnitExtractionPattern = RegExp(r'OU=([a-z
///
/// Will return null if none are found, if the user cancels or if the Xcode
/// project has a development team set in the project's build settings.
Future<Map<String, String>?> getCodeSigningIdentityDevelopmentTeam({
Future<Map<String, String>?> getCodeSigningIdentityDevelopmentTeamBuildSetting({
required Map<String, String>? buildSettings,
required ProcessManager processManager,
required Platform platform,
required Logger logger,
required Config config,
required Terminal terminal,
@ -104,10 +108,10 @@ Future<Map<String, String>?> getCodeSigningIdentityDevelopmentTeam({
// If the user already has it set in the project build settings itself,
// continue with that.
if (_isNotEmpty(buildSettings['DEVELOPMENT_TEAM'])) {
if (_isNotEmpty(buildSettings[_developmentTeamBuildSettingName])) {
logger.printStatus(
'Automatically signing iOS for device deployment using specified development '
'team in Xcode project: ${buildSettings['DEVELOPMENT_TEAM']}'
'team in Xcode project: ${buildSettings[_developmentTeamBuildSettingName]}'
);
return null;
}
@ -116,6 +120,53 @@ Future<Map<String, String>?> getCodeSigningIdentityDevelopmentTeam({
return null;
}
final String? developmentTeam = await _getCodeSigningIdentityDevelopmentTeam(
processManager: processManager,
platform: platform,
logger: logger,
config: config,
terminal: terminal,
shouldExitOnNoCerts: true,
);
if (developmentTeam == null) {
return null;
}
return <String, String>{
_developmentTeamBuildSettingName: developmentTeam,
};
}
Future<String?> getCodeSigningIdentityDevelopmentTeam({
required ProcessManager processManager,
required Platform platform,
required Logger logger,
required Config config,
required Terminal terminal,
}) async =>
_getCodeSigningIdentityDevelopmentTeam(
processManager: processManager,
platform: platform,
logger: logger,
config: config,
terminal: terminal,
shouldExitOnNoCerts: false,
);
/// Set [shouldExitOnNoCerts] to show instructions for how to add a cert when none are found, then [toolExit].
Future<String?> _getCodeSigningIdentityDevelopmentTeam({
required ProcessManager processManager,
required Platform platform,
required Logger logger,
required Config config,
required Terminal terminal,
bool shouldExitOnNoCerts = false,
}) async {
if (!platform.isMacOS) {
return null;
}
// If the user's environment is missing the tools needed to find and read
// certificates, abandon. Tools should be pre-equipped on macOS.
final ProcessUtils processUtils = ProcessUtils(processManager: processManager, logger: logger);
@ -150,7 +201,8 @@ Future<Map<String, String>?> getCodeSigningIdentityDevelopmentTeam({
.toSet() // Unique.
.toList();
final String? signingIdentity = await _chooseSigningIdentity(validCodeSigningIdentities, logger, config, terminal);
final String? signingIdentity =
await _chooseSigningIdentity(validCodeSigningIdentities, logger, config, terminal, shouldExitOnNoCerts);
// If none are chosen, return null.
if (signingIdentity == null) {
@ -193,28 +245,25 @@ Future<Map<String, String>?> getCodeSigningIdentityDevelopmentTeam({
return null;
}
final String? developmentTeam = _certificateOrganizationalUnitExtractionPattern
.firstMatch(opensslOutput)
?.group(1);
if (developmentTeam == null) {
return null;
}
return <String, String>{
'DEVELOPMENT_TEAM': developmentTeam,
};
return _certificateOrganizationalUnitExtractionPattern.firstMatch(opensslOutput)?.group(1);
}
/// Set [shouldExitOnNoCerts] to show instructions for how to add a cert when none are found, then [toolExit].
Future<String?> _chooseSigningIdentity(
List<String> validCodeSigningIdentities,
Logger logger,
Config config,
Terminal terminal,
bool shouldExitOnNoCerts,
) async {
// The user has no valid code signing identities.
if (validCodeSigningIdentities.isEmpty) {
logger.printError(noCertificatesInstruction, emphasis: true);
throwToolExit('No development certificates available to code sign app for device deployment');
if (shouldExitOnNoCerts) {
logger.printError(noCertificatesInstruction, emphasis: true);
throwToolExit('No development certificates available to code sign app for device deployment');
} else {
return null;
}
}
if (validCodeSigningIdentities.length == 1) {

View file

@ -189,8 +189,9 @@ Future<XcodeBuildResult> buildXcodeProject({
) ?? <String, String>{};
if (codesign && environmentType == EnvironmentType.physical) {
autoSigningConfigs = await getCodeSigningIdentityDevelopmentTeam(
autoSigningConfigs = await getCodeSigningIdentityDevelopmentTeamBuildSetting(
buildSettings: buildSettings,
platform: globals.platform,
processManager: globals.processManager,
logger: globals.logger,
config: globals.config,

View file

@ -10,6 +10,7 @@ import 'build_info.dart';
import 'bundle.dart' as bundle;
import 'flutter_plugins.dart';
import 'globals_null_migrated.dart' as globals;
import 'ios/code_signing.dart';
import 'ios/plist_parser.dart';
import 'ios/xcode_build_settings.dart' as xcode;
import 'ios/xcodeproj.dart';
@ -475,12 +476,23 @@ class IosProject extends XcodeBasedProject {
templateRenderer: globals.templateRenderer,
);
final String iosBundleIdentifier = parent.manifest.iosBundleIdentifier ?? 'com.example.${parent.manifest.appName}';
final String? iosDevelopmentTeam = await getCodeSigningIdentityDevelopmentTeam(
processManager: globals.processManager,
platform: globals.platform,
logger: globals.logger,
config: globals.config,
terminal: globals.terminal,
);
template.render(
target,
<String, Object>{
'ios': true,
'projectName': parent.manifest.appName,
'iosIdentifier': iosBundleIdentifier,
'hasIosDevelopmentTeam': iosDevelopmentTeam != null && iosDevelopmentTeam.isNotEmpty,
'iosDevelopmentTeam': iosDevelopmentTeam ?? '',
},
printStatusWhenWriting: false,
overwriteExisting: true,

View file

@ -298,6 +298,9 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
{{#hasIosDevelopmentTeam}}
DEVELOPMENT_TEAM = {{iosDevelopmentTeam}};
{{/hasIosDevelopmentTeam}}
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
@ -421,6 +424,9 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
{{#hasIosDevelopmentTeam}}
DEVELOPMENT_TEAM = {{iosDevelopmentTeam}};
{{/hasIosDevelopmentTeam}}
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
@ -439,6 +445,9 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
{{#hasIosDevelopmentTeam}}
DEVELOPMENT_TEAM = {{iosDevelopmentTeam}};
{{/hasIosDevelopmentTeam}}
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (

View file

@ -288,6 +288,9 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
{{#hasIosDevelopmentTeam}}
DEVELOPMENT_TEAM = {{iosDevelopmentTeam}};
{{/hasIosDevelopmentTeam}}
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
@ -416,6 +419,9 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
{{#hasIosDevelopmentTeam}}
DEVELOPMENT_TEAM = {{iosDevelopmentTeam}};
{{/hasIosDevelopmentTeam}}
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
@ -438,6 +444,9 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
{{#hasIosDevelopmentTeam}}
DEVELOPMENT_TEAM = {{iosDevelopmentTeam}};
{{/hasIosDevelopmentTeam}}
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (

View file

@ -298,6 +298,9 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
{{#hasIosDevelopmentTeam}}
DEVELOPMENT_TEAM = {{iosDevelopmentTeam}};
{{/hasIosDevelopmentTeam}}
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@ -420,6 +423,9 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
{{#hasIosDevelopmentTeam}}
DEVELOPMENT_TEAM = {{iosDevelopmentTeam}};
{{/hasIosDevelopmentTeam}}
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@ -437,6 +443,9 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
{{#hasIosDevelopmentTeam}}
DEVELOPMENT_TEAM = {{iosDevelopmentTeam}};
{{/hasIosDevelopmentTeam}}
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",

View file

@ -129,7 +129,7 @@ void main() {
group('AndroidSdk', () {
testUsingContext('throws throwsToolExit if AndroidSdk is null', () async {
final String projectPath = await createProject(tempDir, arguments: <String>['--no-pub', '--template=app']);
final String projectPath = await createProject(tempDir, arguments: <String>['--no-pub', '--template=app', '--platform=android']);
await expectLater(
() => runBuildApkCommand(
@ -150,7 +150,7 @@ void main() {
});
testUsingContext('shrinking is enabled by default on release mode', () async {
final String projectPath = await createProject(tempDir, arguments: <String>['--no-pub', '--template=app']);
final String projectPath = await createProject(tempDir, arguments: <String>['--no-pub', '--template=app', '--platform=android']);
processManager.addCommand(FakeCommand(
command: <String>[
gradlew,
@ -179,7 +179,7 @@ void main() {
});
testUsingContext('--split-debug-info is enabled when an output directory is provided', () async {
final String projectPath = await createProject(tempDir, arguments: <String>['--no-pub', '--template=app']);
final String projectPath = await createProject(tempDir, arguments: <String>['--no-pub', '--template=app', '--platform=android']);
processManager.addCommand(FakeCommand(
command: <String>[
gradlew,
@ -209,7 +209,7 @@ void main() {
});
testUsingContext('--extra-front-end-options are provided to gradle project', () async {
final String projectPath = await createProject(tempDir, arguments: <String>['--no-pub', '--template=app']);
final String projectPath = await createProject(tempDir, arguments: <String>['--no-pub', '--template=app', '--platform=android']);
processManager.addCommand(FakeCommand(
command: <String>[
gradlew,
@ -239,7 +239,7 @@ void main() {
});
testUsingContext('shrinking is disabled when --no-shrink is passed', () async {
final String projectPath = await createProject(tempDir, arguments: <String>['--no-pub', '--template=app']);
final String projectPath = await createProject(tempDir, arguments: <String>['--no-pub', '--template=app', '--platform=android']);
processManager.addCommand(FakeCommand(
command: <String>[
gradlew,
@ -271,7 +271,7 @@ void main() {
});
testUsingContext('guides the user when the shrinker fails', () async {
final String projectPath = await createProject(tempDir, arguments: <String>['--no-pub', '--template=app']);
final String projectPath = await createProject(tempDir, arguments: <String>['--no-pub', '--template=app', '--platform=android']);
const String r8StdoutWarning =
"Execution failed for task ':app:transformClassesAndResourcesWithR8ForStageInternal'.\n"
'> com.android.tools.r8.CompilationFailedException: Compilation failed to complete';
@ -322,7 +322,7 @@ void main() {
});
testUsingContext("reports when the app isn't using AndroidX", () async {
final String projectPath = await createProject(tempDir, arguments: <String>['--no-pub', '--template=app']);
final String projectPath = await createProject(tempDir, arguments: <String>['--no-pub', '--template=app', '--platform=android']);
// Simulate a non-androidx project.
tempDir
.childDirectory('flutter_project')
@ -375,7 +375,7 @@ void main() {
});
testUsingContext('reports when the app is using AndroidX', () async {
final String projectPath = await createProject(tempDir, arguments: <String>['--no-pub', '--template=app']);
final String projectPath = await createProject(tempDir, arguments: <String>['--no-pub', '--template=app', '--platform=android']);
processManager.addCommand(FakeCommand(
command: <String>[
gradlew,

View file

@ -44,6 +44,9 @@ const String _kDisabledPlatformRequestedMessage = 'currently not supported on yo
// This needs to be created from the local platform due to re-entrant flutter calls made in this test.
FakePlatform _kNoColorTerminalPlatform() => FakePlatform.fromPlatform(const LocalPlatform())..stdoutSupportsAnsi = false;
FakePlatform _kNoColorTerminalMacOSPlatform() => FakePlatform.fromPlatform(const LocalPlatform())
..stdoutSupportsAnsi = false
..operatingSystem = 'macos';
final Map<Type, Generator> noColorTerminalOverride = <Type, Generator>{
Platform: _kNoColorTerminalPlatform,
@ -60,6 +63,7 @@ void main() {
Directory projectDir;
FakeFlutterVersion fakeFlutterVersion;
LoggingProcessManager loggingProcessManager;
FakeProcessManager fakeProcessManager;
BufferLogger logger;
setUpAll(() async {
@ -76,6 +80,7 @@ void main() {
frameworkRevision: frameworkRevision,
channel: frameworkChannel,
);
fakeProcessManager = FakeProcessManager.empty();
});
tearDown(() {
@ -1308,6 +1313,56 @@ void main() {
Platform: _kNoColorTerminalPlatform,
});
testUsingContext('has iOS development team with app template', () async {
Cache.flutterRoot = '../..';
final Completer<void> completer = Completer<void>();
final StreamController<List<int>> controller = StreamController<List<int>>();
const String certificates = '''
1) 86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 "iPhone Developer: Profile 1 (1111AAAA11)"
1 valid identities found''';
fakeProcessManager.addCommands(<FakeCommand>[
const FakeCommand(
command: <String>['which', 'security'],
),
const FakeCommand(
command: <String>['which', 'openssl'],
),
const FakeCommand(
command: <String>['security', 'find-identity', '-p', 'codesigning', '-v'],
stdout: certificates,
),
const FakeCommand(
command: <String>['security', 'find-certificate', '-c', '1111AAAA11', '-p'],
stdout: 'This is a fake certificate',
),
FakeCommand(
command: const <String>['openssl', 'x509', '-subject'],
stdin: IOSink(controller.sink),
stdout: 'subject= /CN=iPhone Developer: Profile 1 (1111AAAA11)/OU=3333CCCC33/O=My Team/C=US',
)
]);
controller.stream.listen((List<int> chunk) {
completer.complete();
});
final CreateCommand command = CreateCommand();
final CommandRunner<void> runner = createTestCommandRunner(command);
await runner.run(<String>['create', '--template=app', '--no-pub', '--org', 'com.foo.bar', projectDir.path]);
final String xcodeProjectPath = globals.fs.path.join('ios', 'Runner.xcodeproj', 'project.pbxproj');
final File xcodeProjectFile = globals.fs.file(globals.fs.path.join(projectDir.path, xcodeProjectPath));
expect(xcodeProjectFile, exists);
final String xcodeProject = xcodeProjectFile.readAsStringSync();
expect(xcodeProject, contains('DEVELOPMENT_TEAM = 3333CCCC33;'));
}, overrides: <Type, Generator>{
FlutterVersion: () => fakeFlutterVersion,
Platform: _kNoColorTerminalMacOSPlatform,
ProcessManager: () => fakeProcessManager,
});
testUsingContext('has correct content and formatting with macOS app template', () async {
Cache.flutterRoot = '../..';

View file

@ -8,6 +8,7 @@ import 'dart:convert';
import 'package:flutter_tools/src/base/config.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/base/terminal.dart';
import 'package:flutter_tools/src/globals_null_migrated.dart' as globals;
import 'package:flutter_tools/src/ios/code_signing.dart';
@ -26,18 +27,21 @@ void main() {
late Config testConfig;
late AnsiTerminal testTerminal;
late BufferLogger logger;
late Platform macosPlatform;
setUp(() async {
logger = BufferLogger.test();
testConfig = Config.test();
testTerminal = TestTerminal();
testTerminal.usesTerminalUi = true;
macosPlatform = FakePlatform(operatingSystem: 'macos');
});
testWithoutContext('No auto-sign if Xcode project settings are not available', () async {
final Map<String, String>? signingConfigs = await getCodeSigningIdentityDevelopmentTeam(
final Map<String, String>? signingConfigs = await getCodeSigningIdentityDevelopmentTeamBuildSetting(
buildSettings: null,
processManager: FakeProcessManager.empty(),
platform: macosPlatform,
logger: logger,
config: testConfig,
terminal: testTerminal,
@ -46,10 +50,11 @@ void main() {
});
testWithoutContext('No discovery if development team specified in Xcode project', () async {
final Map<String, String>? signingConfigs = await getCodeSigningIdentityDevelopmentTeam(
final Map<String, String>? signingConfigs = await getCodeSigningIdentityDevelopmentTeamBuildSetting(
buildSettings: <String, String>{
'DEVELOPMENT_TEAM': 'abc',
},
platform: macosPlatform,
processManager: FakeProcessManager.empty(),
logger: logger,
config: testConfig,
@ -69,16 +74,40 @@ void main() {
),
]);
final Map<String, String>? signingConfigs = await getCodeSigningIdentityDevelopmentTeam(
buildSettings: <String, String>{
'bogus': 'bogus',
},
final String? developmentTeam = await getCodeSigningIdentityDevelopmentTeam(
processManager: processManager,
platform: macosPlatform,
logger: logger,
config: testConfig,
terminal: testTerminal,
);
expect(signingConfigs, isNull);
expect(developmentTeam, isNull);
expect(processManager, hasNoRemainingExpectations);
});
testWithoutContext('No valid code signing certificates', () async {
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
const FakeCommand(
command: <String>['which', 'security'],
),
const FakeCommand(
command: <String>['which', 'openssl'],
),
const FakeCommand(
command: <String>['security', 'find-identity', '-p', 'codesigning', '-v'],
),
]);
final String? developmentTeam = await getCodeSigningIdentityDevelopmentTeam(
processManager: processManager,
platform: macosPlatform,
logger: logger,
config: testConfig,
terminal: testTerminal,
);
expect(developmentTeam, isNull);
expect(processManager, hasNoRemainingExpectations);
});
testWithoutContext('No valid code signing certificates shows instructions', () async {
@ -94,8 +123,9 @@ void main() {
),
]);
await expectLater(() => getCodeSigningIdentityDevelopmentTeam(
await expectLater(() => getCodeSigningIdentityDevelopmentTeamBuildSetting(
buildSettings: <String, String>{},
platform: macosPlatform,
processManager: processManager,
logger: logger,
config: testConfig,
@ -103,7 +133,22 @@ void main() {
), throwsToolExit(message: 'No development certificates available to code sign app for device deployment'));
});
testWithoutContext('Test single identity and certificate organization works', () async {
testWithoutContext('No valid code signing certificates on non-macOS platform', () async {
final FakeProcessManager processManager = FakeProcessManager.empty();
final String? developmentTeam = await getCodeSigningIdentityDevelopmentTeam(
processManager: processManager,
platform: FakePlatform(operatingSystem: 'linux'),
logger: logger,
config: testConfig,
terminal: testTerminal,
);
expect(developmentTeam, isNull);
expect(processManager, hasNoRemainingExpectations);
});
testWithoutContext('Test single identity and certificate organization development team build setting', () async {
final Completer<void> completer = Completer<void>();
final StreamController<List<int>> controller = StreamController<List<int>>();
const String certificates = '''
@ -132,17 +177,18 @@ void main() {
)
]);
// Verify that certifacte value is passed into openssl command.
// Verify that certificate value is passed into openssl command.
String? stdin;
controller.stream.listen((List<int> chunk) {
stdin = utf8.decode(chunk);
completer.complete();
});
final Map<String, String>? signingConfigs = await getCodeSigningIdentityDevelopmentTeam(
final Map<String, String>? signingConfigs = await getCodeSigningIdentityDevelopmentTeamBuildSetting(
buildSettings: <String, String>{
'bogus': 'bogus',
},
platform: macosPlatform,
processManager: processManager,
logger: logger,
config: testConfig,
@ -153,6 +199,58 @@ void main() {
expect(logger.errorText, isEmpty);
expect(stdin, 'This is a fake certificate');
expect(signingConfigs, <String, String>{'DEVELOPMENT_TEAM': '3333CCCC33'});
expect(processManager, hasNoRemainingExpectations);
});
testWithoutContext('Test single identity and certificate organization development team', () async {
final Completer<void> completer = Completer<void>();
final StreamController<List<int>> controller = StreamController<List<int>>();
const String certificates = '''
1) 86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 "iPhone Developer: Profile 1 (1111AAAA11)"
1 valid identities found''';
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
const FakeCommand(
command: <String>['which', 'security'],
),
const FakeCommand(
command: <String>['which', 'openssl'],
),
const FakeCommand(
command: <String>['security', 'find-identity', '-p', 'codesigning', '-v'],
stdout: certificates,
),
const FakeCommand(
command: <String>['security', 'find-certificate', '-c', '1111AAAA11', '-p'],
stdout: 'This is a fake certificate',
),
FakeCommand(
command: const <String>['openssl', 'x509', '-subject'],
stdin: IOSink(controller.sink),
stdout: 'subject= /CN=iPhone Developer: Profile 1 (1111AAAA11)/OU=3333CCCC33/O=My Team/C=US',
completer: completer,
)
]);
// Verify that certificate value is passed into openssl command.
String? stdin;
controller.stream.listen((List<int> chunk) {
stdin = utf8.decode(chunk);
completer.complete();
});
final String? developmentTeam = await getCodeSigningIdentityDevelopmentTeam(
processManager: processManager,
platform: macosPlatform,
logger: logger,
config: testConfig,
terminal: testTerminal,
);
expect(logger.statusText, contains('iPhone Developer: Profile 1 (1111AAAA11)'));
expect(logger.errorText, isEmpty);
expect(stdin, 'This is a fake certificate');
expect(developmentTeam, '3333CCCC33');
expect(processManager, hasNoRemainingExpectations);
});
testWithoutContext('Test single identity (Catalina format) and certificate organization works', () async {
@ -184,18 +282,16 @@ void main() {
)
]);
// Verify that certifacte value is passed into openssl command.
// Verify that certificate value is passed into openssl command.
String? stdin;
controller.stream.listen((List<int> chunk) {
stdin = utf8.decode(chunk);
completer.complete();
});
final Map<String, String>? signingConfigs = await getCodeSigningIdentityDevelopmentTeam(
buildSettings: <String, String>{
'bogus': 'bogus',
},
final String? developmentTeam = await getCodeSigningIdentityDevelopmentTeam(
processManager: processManager,
platform: macosPlatform,
logger: logger,
config: testConfig,
terminal: testTerminal,
@ -204,7 +300,8 @@ void main() {
expect(logger.statusText, contains('Apple Development: Profile 1 (1111AAAA11)'));
expect(logger.errorText, isEmpty);
expect(stdin, 'This is a fake certificate');
expect(signingConfigs, <String, String>{'DEVELOPMENT_TEAM': '3333CCCC33'});
expect(developmentTeam, '3333CCCC33');
expect(processManager, hasNoRemainingExpectations);
});
testWithoutContext('Test multiple identity and certificate organization works', () async {
@ -234,18 +331,16 @@ void main() {
)
]);
// Verify that certifacte value is passed into openssl command.
// Verify that certificate value is passed into openssl command.
String? stdin;
controller.stream.listen((List<int> chunk) {
stdin = utf8.decode(chunk);
completer.complete();
});
final Map<String, String>? signingConfigs = await getCodeSigningIdentityDevelopmentTeam(
buildSettings: <String, String>{
'bogus': 'bogus',
},
final String? developmentTeam = await getCodeSigningIdentityDevelopmentTeam(
processManager: processManager,
platform: macosPlatform,
logger: logger,
config: testConfig,
terminal: testTerminal,
@ -261,8 +356,9 @@ void main() {
);
expect(logger.errorText, isEmpty);
expect(stdin, 'This is a fake certificate');
expect(signingConfigs, <String, String>{'DEVELOPMENT_TEAM': '4444DDDD44'});
expect(developmentTeam, '4444DDDD44');
expect(testConfig.getValue('ios-signing-cert'), 'iPhone Developer: Profile 3 (3333CCCC33)');
expect(processManager, hasNoRemainingExpectations);
});
testWithoutContext('Test multiple identity in machine mode works', () async {
@ -292,18 +388,16 @@ void main() {
)
]);
// Verify that certifacte value is passed into openssl command.
// Verify that certificate value is passed into openssl command.
String? stdin;
controller.stream.listen((List<int> chunk) {
stdin = utf8.decode(chunk);
completer.complete();
});
final Map<String, String>? signingConfigs = await getCodeSigningIdentityDevelopmentTeam(
buildSettings: <String, String>{
'bogus': 'bogus',
},
final String? developmentTeam = await getCodeSigningIdentityDevelopmentTeam(
processManager: processManager,
platform: macosPlatform,
logger: logger,
config: testConfig,
terminal: testTerminal,
@ -315,7 +409,8 @@ void main() {
);
expect(logger.errorText, isEmpty);
expect(stdin, 'This is a fake certificate');
expect(signingConfigs, <String, String>{'DEVELOPMENT_TEAM': '5555EEEE55'});
expect(developmentTeam, '5555EEEE55');
expect(processManager, hasNoRemainingExpectations);
});
testWithoutContext('Test saved certificate used', () async {
@ -345,18 +440,16 @@ void main() {
)
]);
// Verify that certifacte value is passed into openssl command.
// Verify that certificate value is passed into openssl command.
String? stdin;
controller.stream.listen((List<int> chunk) {
stdin = utf8.decode(chunk);
completer.complete();
});
final Map<String, String>? signingConfigs = await getCodeSigningIdentityDevelopmentTeam(
buildSettings: <String, String>{
'bogus': 'bogus',
},
final String? developmentTeam = await getCodeSigningIdentityDevelopmentTeam(
processManager: processManager,
platform: macosPlatform,
logger: logger,
config: testConfig,
terminal: testTerminal,
@ -372,7 +465,8 @@ void main() {
);
expect(logger.errorText, isEmpty);
expect(stdin, 'This is a fake certificate');
expect(signingConfigs, <String, String>{'DEVELOPMENT_TEAM': '4444DDDD44'});
expect(developmentTeam, '4444DDDD44');
expect(processManager, hasNoRemainingExpectations);
});
testWithoutContext('Test invalid saved certificate shows error and prompts again', () async {
@ -403,18 +497,16 @@ void main() {
)
]);
// Verify that certifacte value is passed into openssl command.
// Verify that certificate value is passed into openssl command.
String? stdin;
controller.stream.listen((List<int> chunk) {
stdin = utf8.decode(chunk);
completer.complete();
});
final Map<String, String>? signingConfigs = await getCodeSigningIdentityDevelopmentTeam(
buildSettings: <String, String>{
'bogus': 'bogus',
},
final String? developmentTeam = await getCodeSigningIdentityDevelopmentTeam(
processManager: processManager,
platform: macosPlatform,
logger: logger,
config: testConfig,
terminal: testTerminal,
@ -428,9 +520,10 @@ void main() {
logger.statusText,
contains('Certificate choice "iPhone Developer: Profile 3 (3333CCCC33)"'),
);
expect(signingConfigs, <String, String>{'DEVELOPMENT_TEAM': '4444DDDD44'});
expect(developmentTeam, '4444DDDD44');
expect(stdin, 'This is a fake certificate');
expect(testConfig.getValue('ios-signing-cert'), 'iPhone Developer: Profile 3 (3333CCCC33)');
expect(processManager, hasNoRemainingExpectations);
});
testWithoutContext('find-identity failure', () async {
@ -447,16 +540,15 @@ void main() {
),
]);
final Map<String, String>? signingConfigs = await getCodeSigningIdentityDevelopmentTeam(
buildSettings: <String, String>{
'bogus': 'bogus',
},
final String? developmentTeam = await getCodeSigningIdentityDevelopmentTeam(
processManager: processManager,
platform: macosPlatform,
logger: logger,
config: testConfig,
terminal: testTerminal,
);
expect(signingConfigs, isNull);
expect(developmentTeam, isNull);
expect(processManager, hasNoRemainingExpectations);
});
testWithoutContext('find-certificate failure', () async {
@ -479,16 +571,15 @@ void main() {
),
]);
final Map<String, String>? signingConfigs = await getCodeSigningIdentityDevelopmentTeam(
buildSettings: <String, String>{
'bogus': 'bogus',
},
final String? developmentTeam = await getCodeSigningIdentityDevelopmentTeam(
processManager: processManager,
platform: macosPlatform,
logger: logger,
config: testConfig,
terminal: testTerminal,
);
expect(signingConfigs, isNull);
expect(developmentTeam, isNull);
expect(processManager, hasNoRemainingExpectations);
});
});
}