From 1dc68f2baeb7b89f917cb96a77a04672b60dd12b Mon Sep 17 00:00:00 2001 From: Yehuda Kremer Date: Sat, 29 Oct 2022 23:55:54 +0300 Subject: [PATCH 01/13] fix using signTool options --- CHANGELOG.md | 6 +- README.md | 16 +-- example/README.md | 38 ++++- lib/msix.dart | 8 +- lib/src/command_line_converter.dart | 64 +++++++++ lib/src/configuration.dart | 16 ++- lib/src/extensions.dart | 19 ++- lib/src/makeappx.dart | 6 +- lib/src/makepri.dart | 10 +- lib/src/sign_tool.dart | 213 ++++++++++++++++++++-------- lib/src/windows_build.dart | 6 +- pubspec.yaml | 2 +- 12 files changed, 311 insertions(+), 93 deletions(-) create mode 100644 lib/src/command_line_converter.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 49be986..dfafecc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 3.7.0 + +- add full support for [SignTool](https://learn.microsoft.com/en-us/dotnet/framework/tools/signtool-exe) usage (addressing [#155](https://github.com/YehudaKremer/msix/pull/155#issue-1421291620)), see [examples page](https://github.com/YehudaKremer/msix/tree/main/example) + ## 3.6.6 - replacing cli_dialog package with console package to solve transitive dependencies [https://github.com/timsneath/dart_console/issues/54](https://github.com/timsneath/dart_console/issues/54) @@ -85,7 +89,7 @@ ## 3.0.0 -- add `publish` command and configurations, for sideloading publish (outside the microsoft store) +- add `publish` command and configurations, for side loading publish (outside the microsoft store) - user asked (cli dialog) if he want to **increment version number** (if needed) - user asked (cli dialog) if he want to install the test certificate - add [toast notifications](https://github.com/YehudaKremer/msix/issues/94) configuration diff --git a/README.md b/README.md index 8540695..666d3d0 100644 --- a/README.md +++ b/README.md @@ -98,14 +98,14 @@ See [Configurations Examples And Use Cases].
Sign configuration (click to expand) -| YAML name | Command-line argument | Description | Example | -| ---------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------- | -| `certificate_path` | `--certificate-path` `-c` | Path to the certificate content to place in the store. | `C:\certs\signcert.pfx` | -| `certificate_password` | `--certificate-password` `-p` | Password for the certificate. | `1234` | -| `publisher` | `--publisher` `-b` | The `Subject` value in the certificate. | `CN=BF212345-5644-46DF-8668-014043C1B138` or `CN=Contoso Software, O=Contoso Corporation, C=US` | -| `signtool_options` | `--signtool-options` | Options to be provided to the `signtool` for app signing (see below.) | `/v /fd SHA256 /f C:/Users/me/Desktop/my.cer` | -| `sign_msix` | `--sign-msix ` | If `false`, don't sign the msix file, default is `true`.
Note: when **false**, `publisher` is Required. | `true` | -| `install_certificate` | `--install-certificate ` | If `false`, don't try to install the certificate, default is `true`. | `true` | +| YAML name | Command-line argument | Description | Example | +| ---------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | +| `certificate_path` | `--certificate-path` `-c` | Path to the certificate content to place in the store. | `C:\certs\signcert.pfx` | +| `certificate_password` | `--certificate-password` `-p` | Password for the certificate. | `1234` | +| `publisher` | `--publisher` `-b` | The `Subject` value in the certificate.
Required only if publish to the store, or if the `Publisher` not found by this package. | `CN=BF212345-5644-46DF-8668-014043C1B138` or `CN=Contoso Software, O=Contoso Corporation, C=US` | +| `signtool_options` | `--signtool-options` | Options to be provided to the `signtool` for app signing (see below.) | `/v /fd SHA256 /f C:/Users/me/Desktop/my.cer` | +| `sign_msix` | `--sign-msix ` | If `false`, don't sign the msix file, default is `true`.
Note: when **false**, `publisher` is Required. | `true` | +| `install_certificate` | `--install-certificate ` | If `false`, don't try to install the certificate, default is `true`. | `true` |
diff --git a/example/README.md b/example/README.md index d262b25..fc175a5 100644 --- a/example/README.md +++ b/example/README.md @@ -15,7 +15,7 @@ msix_config: msix_config: display_name: Flutter App publisher_display_name: Company Name - identity_name: 48434MySoftware.MyFlutterApp + identity_name: 48434MySoftware.MyFlutterApp publisher: CN=BF212345-5644-46DF-8668-012044C1B138 msix_version: 1.0.1.0 store: true @@ -43,7 +43,10 @@ msix_config: display_name: Flutter App install_certificate: false ``` -Note: The main app version will be used as a basis for the MSIX version (`1.3.2` to `1.3.2.0`) + +##### Note: The main app version will be used as a basis for the MSIX version (`1.3.2` to `1.3.2.0`) + +
###### Without signing @@ -64,3 +67,34 @@ msix_config: languages: en-us, de-de capabilities: "internetClient,location,microphone,webcam" ``` + +#### Signing With Installed Certificate Using SignTool + +
+ +###### By Subject: + +```yaml +msix_config: + display_name: Flutter App + msix_version: 1.0.0.0 + signtool_options: /v /debug /sm /fd sha256 /n "Msix Testing" /tr http://timestamp.digicert.com +``` + +###### By Issuer: + +```yaml +msix_config: + display_name: Flutter App + msix_version: 1.0.0.0 + signtool_options: /fd sha256 /i "Msix Testing" /tr http://timestamp.digicert.com +``` + +###### By Thumbprint: + +```yaml +msix_config: + display_name: Flutter App + msix_version: 1.0.0.0 + signtool_options: /fd sha256 /sha1 028bc9922d198ee83d776aa19cb8e82897691e0c /tr http://timestamp.digicert.com +``` diff --git a/lib/msix.dart b/lib/msix.dart index 1a44f1c..7b47b65 100644 --- a/lib/msix.dart +++ b/lib/msix.dart @@ -128,7 +128,13 @@ class Msix { if (_config.signMsix && !_config.store) { final signTool = SignTool(); - if (_config.installCert) await signTool.installCertificate(); + + if (_config.installCert && + (_config.signToolOptions == null || + _config.signToolOptions!.isEmpty)) { + await signTool.installCertificate(); + } + await signTool.sign(); } diff --git a/lib/src/command_line_converter.dart b/lib/src/command_line_converter.dart new file mode 100644 index 0000000..fe4d904 --- /dev/null +++ b/lib/src/command_line_converter.dart @@ -0,0 +1,64 @@ +import 'dart:convert'; + +/// Splits a `String` into a list of command-line argument parts. +/// e.g. "command -p param" -> ["command", "-p", "param"] +/// from: https://stackoverflow.com/a/61409439/4480046 +class CommandLineConverter extends Converter> { + @override + List convert(String input) { + if (input.isEmpty) { + //no command? no string + return []; + } + + final List result = []; + + var current = ""; + + String? inQuote; + bool lastTokenHasBeenQuoted = false; + + for (int index = 0; index < input.length; index++) { + final token = input[index]; + + if (inQuote != null) { + if (token == inQuote) { + lastTokenHasBeenQuoted = true; + inQuote = null; + } else { + current += token; + } + } else { + switch (token) { + case "'": // ' + case '"': // "" + + inQuote = token; + continue; + + case " ": // space + + if (lastTokenHasBeenQuoted || current.isNotEmpty) { + result.add(current); + current = ""; + } + break; + + default: + current += token; + lastTokenHasBeenQuoted = false; + } + } + } + + if (lastTokenHasBeenQuoted || current.isNotEmpty) { + result.add(current); + } + + if (inQuote != null) { + throw Exception("Unbalanced quote $inQuote in input:\n$input"); + } + + return result; + } +} diff --git a/lib/src/configuration.dart b/lib/src/configuration.dart index d094d26..f2e629b 100644 --- a/lib/src/configuration.dart +++ b/lib/src/configuration.dart @@ -6,6 +6,7 @@ import 'package:package_config/package_config.dart' show findPackageConfig; import 'package:path/path.dart' show extension, basename; import 'package:pub_semver/pub_semver.dart'; import 'package:yaml/yaml.dart' show YamlMap, loadYaml; +import 'command_line_converter.dart'; import 'extensions.dart'; /// Handles loading and validating the configuration values @@ -109,11 +110,14 @@ class Configuration { publisher = _args['publisher'] ?? yaml['publisher']; identityName = _args['identity-name'] ?? yaml['identity_name']; logoPath = _args['logo-path'] ?? yaml['logo_path']; - signToolOptions = (_args['signtool-options'] ?? yaml['signtool_options']) - ?.toString() - .split(' ') - .where((o) => o.trim().isNotEmpty) - .toList(); + final signToolOptionsConfig = + (_args['signtool-options'] ?? yaml['signtool_options'])?.toString(); + if (signToolOptionsConfig != null && signToolOptionsConfig.isNotEmpty) { + var commandLineConverter = CommandLineConverter(); + signToolOptions = commandLineConverter.convert(signToolOptionsConfig); + } + + //CommandLineConverter protocolActivation = _getProtocolsActivation(yaml); fileExtension = _args['file-extension'] ?? yaml['file_extension']; if (fileExtension != null && !fileExtension!.startsWith('.')) { @@ -227,7 +231,7 @@ class Configuration { throw 'The file certificate not found in: $certificatePath, check "msix_config: certificate_path" at pubspec.yaml'; } - if (extension(certificatePath!) == '.pfx' && + if (extension(certificatePath!).toLowerCase() == '.pfx' && certificatePassword.isNull) { throw 'Certificate password is empty, check "msix_config: certificate_password" at pubspec.yaml'; } diff --git a/lib/src/extensions.dart b/lib/src/extensions.dart index d8afb96..0f4de54 100644 --- a/lib/src/extensions.dart +++ b/lib/src/extensions.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'dart:convert' show HtmlEscape; -import 'package:cli_util/cli_logging.dart' show Ansi; +import 'package:cli_util/cli_logging.dart' show Ansi, Logger; +import 'package:get_it/get_it.dart'; import 'package:path/path.dart' as path; extension StringValidations on String? { @@ -17,6 +18,13 @@ extension StringExtensions on String { String get gray => '${Ansi(true).gray}${this}${Ansi(true).none}'; } +extension StringListExtensions on List { + bool containsArgument(String arg) => + any((item) => item.toLowerCase().trim() == arg); + bool containsArguments(List args) => + any((item) => args.contains(item.toLowerCase().trim())); +} + extension StringConversions on String? { String? toHtmlEscape() => this != null ? const HtmlEscape().convert(this!) : null; @@ -43,3 +51,12 @@ extension DirectoryExtensions on Directory { } } } + +extension ProcessResultExtensions on ProcessResult { + void exitOnError() { + if (this.exitCode != 0) { + GetIt.I().stderr(this.stdout); + throw this.stderr; + } + } +} diff --git a/lib/src/makeappx.dart b/lib/src/makeappx.dart index 3dc65cb..86e69ed 100644 --- a/lib/src/makeappx.dart +++ b/lib/src/makeappx.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:cli_util/cli_logging.dart' show Logger; import 'package:get_it/get_it.dart'; +import 'package:msix/src/extensions.dart'; import 'configuration.dart'; /// Use the makeappx.exe tool to generate manifest file @@ -24,9 +25,6 @@ class MakeAppx { _config.msixPath ]); - if (makeAppxProcess.exitCode != 0) { - _logger.stderr(makeAppxProcess.stdout); - throw makeAppxProcess.stderr; - } + makeAppxProcess.exitOnError(); } } diff --git a/lib/src/makepri.dart b/lib/src/makepri.dart index b78d359..c79b588 100644 --- a/lib/src/makepri.dart +++ b/lib/src/makepri.dart @@ -25,10 +25,7 @@ class MakePri { '/o' ]); - if (makePriConfigProcess.exitCode != 0) { - _logger.stderr(makePriConfigProcess.stdout); - throw makePriConfigProcess.stderr; - } + makePriConfigProcess.exitOnError(); var makePriProcess = await Process.run(makePriPath, [ 'new', @@ -45,9 +42,6 @@ class MakePri { await File('$buildPath/priconfig.xml').deleteIfExists(); - if (makePriProcess.exitCode != 0) { - _logger.stderr(makePriProcess.stdout); - throw makePriProcess.stderr; - } + makePriProcess.exitOnError(); } } diff --git a/lib/src/sign_tool.dart b/lib/src/sign_tool.dart index e9daa6c..7dfe447 100644 --- a/lib/src/sign_tool.dart +++ b/lib/src/sign_tool.dart @@ -15,44 +15,138 @@ class SignTool { final Logger _logger = GetIt.I(); final Configuration _config = GetIt.I(); - /// Use Powershell script to get the Publisher ("Subject") of the certificate + /// get the certificate "Subject" for the Publisher value Future getCertificatePublisher() async { _logger.trace('getting certificate publisher'); + var subject = ''; + + if (_config.signToolOptions != null) { + if (_config.signToolOptions!.containsArgument('/sha1')) { + subject = await _getCertificateSubjectByThumbprint(); + } else if (_config.signToolOptions!.containsArguments(['/n', '/r'])) { + subject = await _getCertificateSubjectBySubject(); + } else if (_config.signToolOptions!.containsArgument('/i')) { + subject = await _getCertificateSubjectByIssuer(); + } + } else if (_config.certificatePath != null && + extension(_config.certificatePath!).toLowerCase() == '.pfx') { + subject = await _getPfxCertificateSubject(); + } + + if (subject.isNotEmpty) { + _config.publisher = subject; + } + + if (_config.publisher != null && _config.publisher!.isNotEmpty) { + _checkCertificateSubject(_config.publisher!); + } else { + _failToGetCertificateSubject(); + } + } + + /// prints useful information on the Publisher value messing + /// and exit the program + void _failToGetCertificateSubject() { + _logger.stdout('could not find the Publisher value.'.red); + _logger.stdout( + 'you must provide the Publisher value at "msix_config: publisher" in the pubspec.yaml file' + .red); + _logger.stdout( + 'the Publisher is the certificate "Subject" in this exact format: "CN=Contoso Software, O=Contoso Corporation, C=US"'); + _logger.stdout('see where you can found your certificate Subject:'); + _logger.stdout( + 'https://drive.google.com/file/d/1oAsnrp2Kf-jZ_kaRjyF5llQ0YZy1IwNe/view?usp=sharing' + .blue); + exit(-1); + } + + String _getSignToolOptionsArgumentValue(String searchArgName) { + var argumentIndex = _config.signToolOptions!.indexWhere( + (argument) => argument.toLowerCase().trim() == searchArgName); + + /// return argument value + return _config.signToolOptions![argumentIndex + 1]; + } + + void _checkCertificateSubject(String subject) { + if (subject.isNotEmpty && !_publisherRegex.hasMatch(subject)) { + throw 'invalid certificate subject: $subject'; + } + } + + Future _executePowershellCommand(String command) async { + return await Process.run( + 'powershell.exe', ['-NoProfile', '-NonInteractive', command], + stdoutEncoding: utf8, stderrEncoding: utf8); + } + + Future _getInstalledCertificateSubject(String searchCondition) async { + var certificateDetailsProcess = await _executePowershellCommand( + "dir -Recurse cert: | where {$searchCondition} | select -expandproperty Subject -First 1"); + + certificateDetailsProcess.exitOnError(); + + var subject = (certificateDetailsProcess.stdout as String).trim(); + + return subject; + } + + Future _getCertificateSubjectByThumbprint() async { + _logger.trace('getting certificate "Subject" by certificate thumbprint'); + + var thumbprintValue = _getSignToolOptionsArgumentValue('/sha1'); + var subject = await _getInstalledCertificateSubject( + "\$_.Thumbprint -eq \"$thumbprintValue\""); + + return subject; + } + + Future _getCertificateSubjectBySubject() async { + _logger.trace('getting certificate "Subject" by certificate Subject'); + + var subjectValue = _getSignToolOptionsArgumentValue('/n'); + var subject = await _getInstalledCertificateSubject( + "\$_.Subject –like \"*$subjectValue*\""); + + return subject; + } + + Future _getCertificateSubjectByIssuer() async { + _logger.trace('getting certificate "Subject" by certificate Issuer'); + + var subjectValue = _getSignToolOptionsArgumentValue('/i'); + var subject = await _getInstalledCertificateSubject( + "\$_.Issuer –like \"*$subjectValue*\""); + + return subject; + } + + Future _getPfxCertificateSubject() async { + _logger.trace('getting pfx certificate Subject'); + var powershellSubjectOutputFilePath = "${_config.msixAssetsPath}/subject.txt"; - var certificateDetailsProcess = await Process.run( - 'powershell.exe', - [ - '-NoProfile', - '-NonInteractive', - "(Get-PfxData -FilePath \"${_config.certificatePath}\" -Password \$(ConvertTo-SecureString -String \"${_config.certificatePassword}\" -AsPlainText -Force)).EndEntityCertificates[0] | Format-List -Property Subject | Out-File -NoNewLine -Width 8192 -Encoding UTF8 -FilePath \"$powershellSubjectOutputFilePath\"" - ], - stdoutEncoding: utf8, - stderrEncoding: utf8); - - if (certificateDetailsProcess.exitCode != 0) { - _logger.stderr(certificateDetailsProcess.stdout); - throw certificateDetailsProcess.stderr; - } + var certificateDetailsProcess = await _executePowershellCommand( + "(Get-PfxData -FilePath \"${_config.certificatePath}\" -Password \$(ConvertTo-SecureString -String \"${_config.certificatePassword}\" -AsPlainText -Force)).EndEntityCertificates[0] | Format-List -Property Subject | Out-File -NoNewLine -Width 8192 -Encoding UTF8 -FilePath \"$powershellSubjectOutputFilePath\""); + + certificateDetailsProcess.exitOnError(); var powershellSubjectOutputFile = File(powershellSubjectOutputFilePath); if (!await powershellSubjectOutputFile.exists()) { - throw 'cannot get certificate subject'.red; + throw 'cannot get PFX certificate subject'.red; } var subjectRow = await powershellSubjectOutputFile.readAsString(); await powershellSubjectOutputFile.deleteIfExists(); - if (!_publisherRegex.hasMatch(subjectRow)) { - throw 'invalid certificate subject: $subjectRow'; - } - - _config.publisher = subjectRow + String subject = subjectRow .substring(subjectRow.indexOf(':') + 1, subjectRow.length) .trim(); + + return subject; } /// Use Powershell to install the test certificate @@ -64,10 +158,7 @@ class SignTool { "dir Cert:\\CurrentUser\\Root | Where-Object { \$_.Subject -eq '${_config.publisher}'}" ]); - if (getInstalledCertificate.exitCode != 0) { - _logger.stderr(getInstalledCertificate.stdout); - throw getInstalledCertificate.stderr; - } + getInstalledCertificate.exitOnError(); var isCertificateNotInstalled = getInstalledCertificate.stdout.toString().isNullOrEmpty; @@ -117,40 +208,48 @@ class SignTool { Future sign() async { _logger.trace('signing'); - if (!_config.certificatePath.isNull || _config.signToolOptions != null) { - var signtoolPath = - '${_config.msixToolkitPath}/Redist.${_config.architecture}/signtool.exe'; - - List signtoolOptions = []; - - if (_config.signToolOptions != null) { - signtoolOptions = _config.signToolOptions!; - } else { - signtoolOptions = [ - '/v', - '/fd', - 'SHA256', - '/a', - '/f', - _config.certificatePath!, - if (extension(_config.certificatePath!) == '.pfx') '/p', - if (extension(_config.certificatePath!) == '.pfx') - _config.certificatePassword!, - '/tr', - 'http://timestamp.digicert.com' - ]; + var signtoolPath = + '${_config.msixToolkitPath}/Redist.${_config.architecture}/signtool.exe'; + List signtoolOptions = ['/v']; + + if (_config.signToolOptions != null && + _config.signToolOptions!.isNotEmpty) { + signtoolOptions = _config.signToolOptions!; + } else if (_config.certificatePath != null) { + switch (extension(_config.certificatePath!).toLowerCase()) { + case '.pfx': + signtoolOptions.addAll([ + '/fd', + 'SHA256', + '/f', + _config.certificatePath!, + '/p', + _config.certificatePassword! + ]); + break; + default: + signtoolOptions.addAll([ + '/a', + '/fd', + 'SHA256', + '/f', + _config.certificatePath!, + ]); } - ProcessResult signProcess = await Process.run(signtoolPath, [ - 'sign', - ...signtoolOptions, - _config.msixPath, - ]); - - if (signProcess.exitCode != 0) { - _logger.stderr(signProcess.stdout); - throw signProcess.stderr; - } + signtoolOptions.addAll(['/tr', 'http://timestamp.digicert.com']); } + + var isFullSigntoolCommand = + signtoolOptions[0].toLowerCase().contains('signtool'); + + ProcessResult signProcess = await Process.run( + isFullSigntoolCommand ? signtoolOptions[0] : signtoolPath, [ + if (!isFullSigntoolCommand) 'sign', + ...signtoolOptions.skip(isFullSigntoolCommand ? 1 : 0), + _config.msixPath, + ]); + + signProcess.exitOnError(); } } diff --git a/lib/src/windows_build.dart b/lib/src/windows_build.dart index f719759..5c40911 100644 --- a/lib/src/windows_build.dart +++ b/lib/src/windows_build.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:cli_util/cli_logging.dart' show Logger; import 'package:get_it/get_it.dart'; +import 'package:msix/src/extensions.dart'; import 'configuration.dart'; @@ -23,10 +24,7 @@ class WindowsBuild { var windowsBuildProcess = await Process.run('flutter', buildWindowsArguments, runInShell: true); - if (windowsBuildProcess.exitCode != 0) { - _logger.stderr(windowsBuildProcess.stdout); - throw windowsBuildProcess.stderr; - } + windowsBuildProcess.exitOnError(); loggerProgress.finish(showTiming: true); } diff --git a/pubspec.yaml b/pubspec.yaml index 791145e..76e6e87 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: msix description: A command-line tool that create Msix installer from your flutter windows-build files. -version: 3.6.6 +version: 3.7.0 maintainer: Yehuda Kremer (yehudakremer@gmail.com) homepage: https://github.com/YehudaKremer/msix From c3d243ee1e36a7ea00408b7953efd1805db0c43f Mon Sep 17 00:00:00 2001 From: Yehuda Kremer Date: Sun, 30 Oct 2022 00:13:12 +0300 Subject: [PATCH 02/13] update README --- example/README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/example/README.md b/example/README.md index fc175a5..80d6e41 100644 --- a/example/README.md +++ b/example/README.md @@ -70,8 +70,6 @@ msix_config: #### Signing With Installed Certificate Using SignTool -
- ###### By Subject: ```yaml From 114944835b5b57dcecac0a87541941f7c1650632 Mon Sep 17 00:00:00 2001 From: yehudak Date: Mon, 31 Oct 2022 08:35:15 +0200 Subject: [PATCH 03/13] more image to github source --- lib/src/sign_tool.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/sign_tool.dart b/lib/src/sign_tool.dart index 7dfe447..7e2b4ef 100644 --- a/lib/src/sign_tool.dart +++ b/lib/src/sign_tool.dart @@ -56,7 +56,7 @@ class SignTool { 'the Publisher is the certificate "Subject" in this exact format: "CN=Contoso Software, O=Contoso Corporation, C=US"'); _logger.stdout('see where you can found your certificate Subject:'); _logger.stdout( - 'https://drive.google.com/file/d/1oAsnrp2Kf-jZ_kaRjyF5llQ0YZy1IwNe/view?usp=sharing' + 'https://user-images.githubusercontent.com/946652/198945956-ec2ca7f2-36e9-4dfc-959b-48bcd191d82d.png' .blue); exit(-1); } From 21a2b558e10957b74d5d34c3cfff3d5ed06b3ce2 Mon Sep 17 00:00:00 2001 From: Yehuda Kremer Date: Tue, 1 Nov 2022 08:24:34 +0200 Subject: [PATCH 04/13] use X509Certificate2 to get PFX Subject --- lib/src/sign_tool.dart | 29 ++++++++--------------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/lib/src/sign_tool.dart b/lib/src/sign_tool.dart index 7e2b4ef..f8650ee 100644 --- a/lib/src/sign_tool.dart +++ b/lib/src/sign_tool.dart @@ -76,17 +76,19 @@ class SignTool { } Future _executePowershellCommand(String command) async { - return await Process.run( + var processResult = await Process.run( 'powershell.exe', ['-NoProfile', '-NonInteractive', command], stdoutEncoding: utf8, stderrEncoding: utf8); + + processResult.exitOnError(); + + return processResult; } Future _getInstalledCertificateSubject(String searchCondition) async { var certificateDetailsProcess = await _executePowershellCommand( "dir -Recurse cert: | where {$searchCondition} | select -expandproperty Subject -First 1"); - certificateDetailsProcess.exitOnError(); - var subject = (certificateDetailsProcess.stdout as String).trim(); return subject; @@ -125,26 +127,11 @@ class SignTool { Future _getPfxCertificateSubject() async { _logger.trace('getting pfx certificate Subject'); - var powershellSubjectOutputFilePath = - "${_config.msixAssetsPath}/subject.txt"; - var certificateDetailsProcess = await _executePowershellCommand( - "(Get-PfxData -FilePath \"${_config.certificatePath}\" -Password \$(ConvertTo-SecureString -String \"${_config.certificatePassword}\" -AsPlainText -Force)).EndEntityCertificates[0] | Format-List -Property Subject | Out-File -NoNewLine -Width 8192 -Encoding UTF8 -FilePath \"$powershellSubjectOutputFilePath\""); - - certificateDetailsProcess.exitOnError(); + """new-object System.Security.Cryptography.X509Certificates.X509Certificate2("${_config.certificatePath}", + "${_config.certificatePassword}") | select -expandproperty Subject -First 1"""); - var powershellSubjectOutputFile = File(powershellSubjectOutputFilePath); - - if (!await powershellSubjectOutputFile.exists()) { - throw 'cannot get PFX certificate subject'.red; - } - - var subjectRow = await powershellSubjectOutputFile.readAsString(); - await powershellSubjectOutputFile.deleteIfExists(); - - String subject = subjectRow - .substring(subjectRow.indexOf(':') + 1, subjectRow.length) - .trim(); + var subject = (certificateDetailsProcess.stdout as String).trim(); return subject; } From 36fd9893742adb631ea4cfb48f3ad490c0f6c003 Mon Sep 17 00:00:00 2001 From: Yehuda Kremer Date: Tue, 1 Nov 2022 08:34:22 +0200 Subject: [PATCH 05/13] change to explicit type annotation --- lib/msix.dart | 14 ++++----- lib/src/app_installer.dart | 18 +++++------ lib/src/appx_manifest.dart | 12 ++++---- lib/src/assets.dart | 4 +-- lib/src/command_line_converter.dart | 2 +- lib/src/configuration.dart | 26 ++++++++-------- lib/src/extensions.dart | 4 +-- lib/src/makeappx.dart | 4 +-- lib/src/makepri.dart | 6 ++-- lib/src/sign_tool.dart | 47 +++++++++++++++-------------- lib/src/windows_build.dart | 7 +++-- 11 files changed, 73 insertions(+), 71 deletions(-) diff --git a/lib/msix.dart b/lib/msix.dart index 7b47b65..107285d 100644 --- a/lib/msix.dart +++ b/lib/msix.dart @@ -1,6 +1,6 @@ import 'dart:io'; -import 'package:cli_util/cli_logging.dart' show Logger, Ansi; +import 'package:cli_util/cli_logging.dart'; import 'package:get_it/get_it.dart'; import 'src/app_installer.dart'; import 'src/windows_build.dart'; @@ -37,9 +37,9 @@ class Msix { await _initConfig(); /// check if the appx manifest is exist - var appxManifestPath = '${_config.buildFilesFolder}/AppxManifest.xml'; + String appxManifestPath = '${_config.buildFilesFolder}/AppxManifest.xml'; if (!(await File(appxManifestPath).exists())) { - var error = 'run "msix:build" first'; + String error = 'run "msix:build" first'; _logger.stderr(error.red); exit(-1); } @@ -63,12 +63,12 @@ class Msix { Future publish() async { await _initConfig(); await _config.validateAppInstallerConfigValues(); - var appInstaller = AppInstaller(); + AppInstaller appInstaller = AppInstaller(); await appInstaller.validatePublishVersion(); await _createMsix(); - var loggerProgress = _logger.progress('publishing'); + Progress loggerProgress = _logger.progress('publishing'); await appInstaller.copyMsixToVersionsFolder(); await appInstaller.generateAppInstaller(); await appInstaller.generateAppInstallerWebSite(); @@ -103,7 +103,7 @@ class Msix { Future _buildMsixFiles() async { if (_config.buildWindows) await WindowsBuild().build(); - var loggerProgress = _logger.progress('building msix files'); + Progress loggerProgress = _logger.progress('building msix files'); await _config.validateWindowsBuildFiles(); final assets = Assets(); @@ -121,7 +121,7 @@ class Msix { } Future _packMsixFiles() async { - var loggerProgress = _logger.progress('packing msix files'); + Progress loggerProgress = _logger.progress('packing msix files'); await MakeAppx().pack(); await Assets().cleanTemporaryFiles(); diff --git a/lib/src/app_installer.dart b/lib/src/app_installer.dart index 349e4c5..78f47dd 100644 --- a/lib/src/app_installer.dart +++ b/lib/src/app_installer.dart @@ -26,18 +26,18 @@ class AppInstaller { if (!await File(_config.appInstallerPath).exists()) return; - var appInstallerContent = + String appInstallerContent = await File(_config.appInstallerPath).readAsString(); - var appInstallerVersion = appInstallerContent.substring( + String appInstallerVersion = appInstallerContent.substring( appInstallerContent.indexOf('Version="') + 9, appInstallerContent.indexOf( '"', appInstallerContent.indexOf('Version="') + 9)); appInstallerVersion = appInstallerVersion.substring(0, appInstallerVersion.lastIndexOf('.')); - var lastPublishVersion = Version.parse(appInstallerVersion); - var msixVersion = Version.parse(_config.msixVersion! + Version lastPublishVersion = Version.parse(appInstallerVersion); + Version msixVersion = Version.parse(_config.msixVersion! .substring(0, _config.msixVersion!.lastIndexOf('.'))); - var msixVersionRevision = + String msixVersionRevision = _config.msixVersion!.substring(_config.msixVersion!.lastIndexOf('.')); if (lastPublishVersion == msixVersion || lastPublishVersion > msixVersion) { @@ -49,7 +49,7 @@ class AppInstaller { 'You publishing older version ($msixVersion) then last publish version ($lastPublishVersion)'); } - var installCertificate = await readInput( + String installCertificate = await readInput( 'Do you want to increment it to version ${lastPublishVersion.nextPatch} ?' .emphasized + ' (y/N) '.gray); @@ -73,7 +73,7 @@ class AppInstaller { Future generateAppInstaller() async { _logger.trace('generate app installer'); - var appInstallerContent = ''' + String appInstallerContent = ''' diff --git a/lib/src/appx_manifest.dart b/lib/src/appx_manifest.dart index 1cbefa7..01ee801 100644 --- a/lib/src/appx_manifest.dart +++ b/lib/src/appx_manifest.dart @@ -14,7 +14,7 @@ class AppxManifest { Future generateAppxManifest() async { _logger.trace('generate appx manifest'); - var manifestContent = ''' + String manifestContent = ''' @@ -169,13 +169,13 @@ class AppxManifest { String _normalizeCapability(String capability) { capability = capability.trim(); - var firstLetter = capability.substring(0, 1).toLowerCase(); + String firstLetter = capability.substring(0, 1).toLowerCase(); return firstLetter + capability.substring(1); } /// Add capabilities section String _getCapabilities() { - var capabilities = _config.capabilities?.split(',') ?? []; + List capabilities = _config.capabilities?.split(',') ?? []; capabilities.add('runFullTrust'); capabilities = capabilities.toSet().toList(); String capabilitiesString = ''; diff --git a/lib/src/assets.dart b/lib/src/assets.dart index 4afb0f5..9cb8bcc 100644 --- a/lib/src/assets.dart +++ b/lib/src/assets.dart @@ -128,8 +128,8 @@ class Assets { Image imageCanvas = Image(scaledWidth.ceil(), scaledHeight.ceil()); - var drawX = imageCanvas.width ~/ 2 - resizedImage.width ~/ 2; - var drawY = imageCanvas.height ~/ 2 - resizedImage.height ~/ 2; + int drawX = imageCanvas.width ~/ 2 - resizedImage.width ~/ 2; + int drawY = imageCanvas.height ~/ 2 - resizedImage.height ~/ 2; drawImage( imageCanvas, resizedImage, diff --git a/lib/src/command_line_converter.dart b/lib/src/command_line_converter.dart index fe4d904..ff42ba4 100644 --- a/lib/src/command_line_converter.dart +++ b/lib/src/command_line_converter.dart @@ -13,7 +13,7 @@ class CommandLineConverter extends Converter> { final List result = []; - var current = ""; + String current = ""; String? inQuote; bool lastTokenHasBeenQuoted = false; diff --git a/lib/src/configuration.dart b/lib/src/configuration.dart index f2e629b..9dbdd25 100644 --- a/lib/src/configuration.dart +++ b/lib/src/configuration.dart @@ -2,7 +2,7 @@ import 'dart:io'; import 'package:args/args.dart' show ArgParser, ArgResults; import 'package:cli_util/cli_logging.dart' show Logger; import 'package:get_it/get_it.dart'; -import 'package:package_config/package_config.dart' show findPackageConfig; +import 'package:package_config/package_config.dart'; import 'package:path/path.dart' show extension, basename; import 'package:pub_semver/pub_semver.dart'; import 'package:yaml/yaml.dart' show YamlMap, loadYaml; @@ -69,10 +69,10 @@ class Configuration { Future getConfigValues() async { _parseCliArguments(_arguments); await _getMsixAssetsFolderPath(); - var pubspec = await _getPubspec(); + dynamic pubspec = await _getPubspec(); appName = pubspec['name']; appDescription = pubspec['description']; - var yaml = pubspec['msix_config'] ?? YamlMap(); + dynamic yaml = pubspec['msix_config'] ?? YamlMap(); msixVersion = _args['version'] ?? yaml['msix_version'] ?? _getPubspecVersion(pubspec); certificatePath = _args['certificate-path'] ?? yaml['certificate_path']; @@ -113,7 +113,7 @@ class Configuration { final signToolOptionsConfig = (_args['signtool-options'] ?? yaml['signtool_options'])?.toString(); if (signToolOptionsConfig != null && signToolOptionsConfig.isNotEmpty) { - var commandLineConverter = CommandLineConverter(); + CommandLineConverter commandLineConverter = CommandLineConverter(); signToolOptions = commandLineConverter.convert(signToolOptionsConfig); } @@ -131,7 +131,7 @@ class Configuration { yaml['enable_at_startup']?.toString().toLowerCase() == 'true'; // toast activator configurations - var toastActivatorYaml = yaml['toast_activator'] ?? YamlMap(); + dynamic toastActivatorYaml = yaml['toast_activator'] ?? YamlMap(); toastActivatorCLSID = _args['toast-activator-clsid'] ?? toastActivatorYaml['clsid']?.toString(); @@ -143,7 +143,7 @@ class Configuration { 'Toast activator'; // app installer configurations - var installerYaml = yaml['app_installer'] ?? YamlMap(); + dynamic installerYaml = yaml['app_installer'] ?? YamlMap(); publishFolderPath = _args['publish-folder-path'] ?? installerYaml['publish_folder_path']; @@ -175,7 +175,7 @@ class Configuration { throw 'App name is empty, check the general \'name:\' property at pubspec.yaml'; } if (appDescription.isNull) appDescription = appName; - var cleanAppName = appName!.replaceAll('_', ''); + String cleanAppName = appName!.replaceAll('_', ''); if (displayName.isNull) displayName = cleanAppName; if (identityName.isNull) { if (store) { @@ -270,7 +270,7 @@ class Configuration { void _parseCliArguments(List args) { _logger.trace('parsing cli arguments'); - var parser = ArgParser() + ArgParser parser = ArgParser() ..addOption('certificate-password', abbr: 'p') ..addOption('certificate-path', abbr: 'c') ..addOption('version') @@ -327,14 +327,14 @@ class Configuration { /// Get the assets folder path from the .packages file Future _getMsixAssetsFolderPath() async { - var packagesConfig = await findPackageConfig(Directory.current); + PackageConfig? packagesConfig = await findPackageConfig(Directory.current); if (packagesConfig == null) { throw 'Failed to locate or read package config.'; } - var msixPackage = + Package msixPackage = packagesConfig.packages.firstWhere((package) => package.name == "msix"); - var path = + String path = msixPackage.packageUriRoot.toString().replaceAll('file:///', '') + 'assets'; @@ -343,8 +343,8 @@ class Configuration { /// Get pubspec.yaml content dynamic _getPubspec() async { - var pubspecString = await File(pubspecYamlPath).readAsString(); - var pubspec = loadYaml(pubspecString); + String pubspecString = await File(pubspecYamlPath).readAsString(); + dynamic pubspec = loadYaml(pubspecString); return pubspec; } diff --git a/lib/src/extensions.dart b/lib/src/extensions.dart index 0f4de54..31f739c 100644 --- a/lib/src/extensions.dart +++ b/lib/src/extensions.dart @@ -38,9 +38,9 @@ extension FileSystemEntityExtensions on FileSystemEntity { /// Copy directory content asynchronously extension DirectoryExtensions on Directory { Future copyDirectory(Directory destination) async { - await for (var entity in list(recursive: false)) { + await for (FileSystemEntity entity in list(recursive: false)) { if (entity is Directory) { - var newDirectory = Directory( + Directory newDirectory = Directory( path.join(destination.absolute.path, path.basename(entity.path))); await newDirectory.create(); await entity.absolute.copyDirectory(newDirectory); diff --git a/lib/src/makeappx.dart b/lib/src/makeappx.dart index 86e69ed..84041f4 100644 --- a/lib/src/makeappx.dart +++ b/lib/src/makeappx.dart @@ -12,10 +12,10 @@ class MakeAppx { Future pack() async { _logger.trace('packing'); - var makeAppxPath = + String makeAppxPath = '${_config.msixToolkitPath}/Redist.${_config.architecture}/makeappx.exe'; - var makeAppxProcess = await Process.run(makeAppxPath, [ + ProcessResult makeAppxProcess = await Process.run(makeAppxPath, [ 'pack', '/v', '/o', diff --git a/lib/src/makepri.dart b/lib/src/makepri.dart index c79b588..2d61a30 100644 --- a/lib/src/makepri.dart +++ b/lib/src/makepri.dart @@ -13,10 +13,10 @@ class MakePri { _logger.trace('generate package resource indexing files'); final buildPath = _config.buildFilesFolder; - var makePriPath = + String makePriPath = '${_config.msixToolkitPath}/Redist.${_config.architecture}/makepri.exe'; - var makePriConfigProcess = await Process.run(makePriPath, [ + ProcessResult makePriConfigProcess = await Process.run(makePriPath, [ 'createconfig', '/cf', '$buildPath\\priconfig.xml', @@ -27,7 +27,7 @@ class MakePri { makePriConfigProcess.exitOnError(); - var makePriProcess = await Process.run(makePriPath, [ + ProcessResult makePriProcess = await Process.run(makePriPath, [ 'new', '/cf', '$buildPath\\priconfig.xml', diff --git a/lib/src/sign_tool.dart b/lib/src/sign_tool.dart index f8650ee..d724e1d 100644 --- a/lib/src/sign_tool.dart +++ b/lib/src/sign_tool.dart @@ -7,7 +7,7 @@ import 'package:path/path.dart' show extension, basename; import 'extensions.dart'; import 'configuration.dart'; -var _publisherRegex = RegExp( +RegExp _publisherRegex = RegExp( '(CN|L|O|OU|E|C|S|STREET|T|G|I|SN|DC|SERIALNUMBER|(OID.(0|[1-9][0-9]*)(.(0|[1-9][0-9]*))+))=(([^,+="<>#;])+|".*")(, ((CN|L|O|OU|E|C|S|STREET|T|G|I|SN|DC|SERIALNUMBER|(OID.(0|[1-9][0-9]*)(.(0|[1-9][0-9]*))+))=(([^,+="<>#;])+|".*")))*'); /// Handles the certificate sign functionality @@ -19,7 +19,7 @@ class SignTool { Future getCertificatePublisher() async { _logger.trace('getting certificate publisher'); - var subject = ''; + String subject = ''; if (_config.signToolOptions != null) { if (_config.signToolOptions!.containsArgument('/sha1')) { @@ -62,7 +62,7 @@ class SignTool { } String _getSignToolOptionsArgumentValue(String searchArgName) { - var argumentIndex = _config.signToolOptions!.indexWhere( + int argumentIndex = _config.signToolOptions!.indexWhere( (argument) => argument.toLowerCase().trim() == searchArgName); /// return argument value @@ -76,7 +76,7 @@ class SignTool { } Future _executePowershellCommand(String command) async { - var processResult = await Process.run( + ProcessResult processResult = await Process.run( 'powershell.exe', ['-NoProfile', '-NonInteractive', command], stdoutEncoding: utf8, stderrEncoding: utf8); @@ -86,10 +86,10 @@ class SignTool { } Future _getInstalledCertificateSubject(String searchCondition) async { - var certificateDetailsProcess = await _executePowershellCommand( + ProcessResult certificateDetailsProcess = await _executePowershellCommand( "dir -Recurse cert: | where {$searchCondition} | select -expandproperty Subject -First 1"); - var subject = (certificateDetailsProcess.stdout as String).trim(); + String subject = (certificateDetailsProcess.stdout as String).trim(); return subject; } @@ -97,8 +97,8 @@ class SignTool { Future _getCertificateSubjectByThumbprint() async { _logger.trace('getting certificate "Subject" by certificate thumbprint'); - var thumbprintValue = _getSignToolOptionsArgumentValue('/sha1'); - var subject = await _getInstalledCertificateSubject( + String thumbprintValue = _getSignToolOptionsArgumentValue('/sha1'); + String subject = await _getInstalledCertificateSubject( "\$_.Thumbprint -eq \"$thumbprintValue\""); return subject; @@ -107,8 +107,8 @@ class SignTool { Future _getCertificateSubjectBySubject() async { _logger.trace('getting certificate "Subject" by certificate Subject'); - var subjectValue = _getSignToolOptionsArgumentValue('/n'); - var subject = await _getInstalledCertificateSubject( + String subjectValue = _getSignToolOptionsArgumentValue('/n'); + String subject = await _getInstalledCertificateSubject( "\$_.Subject –like \"*$subjectValue*\""); return subject; @@ -117,8 +117,8 @@ class SignTool { Future _getCertificateSubjectByIssuer() async { _logger.trace('getting certificate "Subject" by certificate Issuer'); - var subjectValue = _getSignToolOptionsArgumentValue('/i'); - var subject = await _getInstalledCertificateSubject( + String subjectValue = _getSignToolOptionsArgumentValue('/i'); + String subject = await _getInstalledCertificateSubject( "\$_.Issuer –like \"*$subjectValue*\""); return subject; @@ -127,11 +127,11 @@ class SignTool { Future _getPfxCertificateSubject() async { _logger.trace('getting pfx certificate Subject'); - var certificateDetailsProcess = await _executePowershellCommand( + ProcessResult certificateDetailsProcess = await _executePowershellCommand( """new-object System.Security.Cryptography.X509Certificates.X509Certificate2("${_config.certificatePath}", "${_config.certificatePassword}") | select -expandproperty Subject -First 1"""); - var subject = (certificateDetailsProcess.stdout as String).trim(); + String subject = (certificateDetailsProcess.stdout as String).trim(); return subject; } @@ -139,7 +139,8 @@ class SignTool { /// Use Powershell to install the test certificate /// if needed and if the user want to. Future installCertificate() async { - var getInstalledCertificate = await Process.run('powershell.exe', [ + ProcessResult getInstalledCertificate = + await Process.run('powershell.exe', [ '-NoProfile', '-NonInteractive', "dir Cert:\\CurrentUser\\Root | Where-Object { \$_.Subject -eq '${_config.publisher}'}" @@ -147,29 +148,29 @@ class SignTool { getInstalledCertificate.exitOnError(); - var isCertificateNotInstalled = + bool isCertificateNotInstalled = getInstalledCertificate.stdout.toString().isNullOrEmpty; if (isCertificateNotInstalled) { _logger.trace('installing certificate'); _logger.stdout(''); - var installCertificate = await readInput( + String installCertificate = await readInput( 'Do you want to install the certificate: "${basename(File(_config.certificatePath!).path)}" ?' .emphasized + ' (y/N) '.gray); if (installCertificate.toLowerCase().trim() == 'y') { // create installCertificate.ps1 file - var installCertificateScript = + String installCertificateScript = 'Import-PfxCertificate -FilePath "${_config.certificatePath}" -Password (ConvertTo-SecureString -String "${_config.certificatePassword}" -AsPlainText -Force) -CertStoreLocation Cert:\\LocalMachine\\Root'; - var installCertificateScriptPath = + String installCertificateScriptPath = '${_config.msixAssetsPath}/installCertificate.ps1'; await File(installCertificateScriptPath) .writeAsString(installCertificateScript); // then execute it with admin privileges - var importCertificate = await Process.run('powershell.exe', [ + ProcessResult importCertificate = await Process.run('powershell.exe', [ '-NoProfile', '-NonInteractive', 'Start-Process powershell -ArgumentList "$installCertificateScriptPath" -Wait -Verb runAs -WindowStyle Hidden' @@ -178,7 +179,7 @@ class SignTool { await File(installCertificateScriptPath).deleteIfExists(); if (importCertificate.exitCode != 0) { - var error = importCertificate.stderr.toString(); + String error = importCertificate.stderr.toString(); if (error.contains('was canceled by the user')) { _logger.stderr('the certificate installation was canceled'.red); } else { @@ -195,7 +196,7 @@ class SignTool { Future sign() async { _logger.trace('signing'); - var signtoolPath = + String signtoolPath = '${_config.msixToolkitPath}/Redist.${_config.architecture}/signtool.exe'; List signtoolOptions = ['/v']; @@ -227,7 +228,7 @@ class SignTool { signtoolOptions.addAll(['/tr', 'http://timestamp.digicert.com']); } - var isFullSigntoolCommand = + bool isFullSigntoolCommand = signtoolOptions[0].toLowerCase().contains('signtool'); ProcessResult signProcess = await Process.run( diff --git a/lib/src/windows_build.dart b/lib/src/windows_build.dart index 5c40911..c8714b0 100644 --- a/lib/src/windows_build.dart +++ b/lib/src/windows_build.dart @@ -1,5 +1,6 @@ import 'dart:io'; import 'package:cli_util/cli_logging.dart' show Logger; +import 'package:cli_util/cli_logging.dart'; import 'package:get_it/get_it.dart'; import 'package:msix/src/extensions.dart'; @@ -15,13 +16,13 @@ class WindowsBuild { /// Run "flutter build windows" command Future build() async { - var buildWindowsArguments = ['build', 'windows']; + List buildWindowsArguments = ['build', 'windows']; if (_config.createWithDebugBuildFiles) buildWindowsArguments.add('--debug'); - var loggerProgress = _logger + Progress loggerProgress = _logger .progress('running "flutter ${buildWindowsArguments.join(' ')}"'); - var windowsBuildProcess = + ProcessResult windowsBuildProcess = await Process.run('flutter', buildWindowsArguments, runInShell: true); windowsBuildProcess.exitOnError(); From 8a728de81072d9e02712b3a9a38408b578e3f716 Mon Sep 17 00:00:00 2001 From: Yehuda Kremer Date: Tue, 1 Nov 2022 08:36:28 +0200 Subject: [PATCH 06/13] remove shows --- lib/src/app_installer.dart | 11 +++++------ lib/src/appx_manifest.dart | 2 +- lib/src/assets.dart | 4 ++-- lib/src/configuration.dart | 8 ++++---- lib/src/extensions.dart | 4 ++-- lib/src/makeappx.dart | 2 +- lib/src/makepri.dart | 2 +- lib/src/sign_tool.dart | 4 ++-- lib/src/windows_build.dart | 1 - 9 files changed, 18 insertions(+), 20 deletions(-) diff --git a/lib/src/app_installer.dart b/lib/src/app_installer.dart index 78f47dd..498070b 100644 --- a/lib/src/app_installer.dart +++ b/lib/src/app_installer.dart @@ -1,13 +1,12 @@ import 'dart:io'; -import 'dart:convert' show HtmlEscape, base64Encode; +import 'dart:convert'; import 'package:console/console.dart'; import 'package:get_it/get_it.dart'; -import 'package:image/image.dart' - show Image, copyResize, decodeImage, encodePng, trim; +import 'package:image/image.dart'; import 'package:msix/src/extensions.dart'; -import 'package:path/path.dart' show basename; -import 'package:pub_semver/pub_semver.dart' show Version; -import 'package:cli_util/cli_logging.dart' show Logger; +import 'package:path/path.dart'; +import 'package:pub_semver/pub_semver.dart'; +import 'package:cli_util/cli_logging.dart'; import 'configuration.dart'; /// Handles the creation of .appinstaller file and msix versions diff --git a/lib/src/appx_manifest.dart b/lib/src/appx_manifest.dart index 01ee801..22abec0 100644 --- a/lib/src/appx_manifest.dart +++ b/lib/src/appx_manifest.dart @@ -1,5 +1,5 @@ import 'dart:io'; -import 'package:cli_util/cli_logging.dart' show Logger; +import 'package:cli_util/cli_logging.dart'; import 'package:get_it/get_it.dart'; import 'capabilities.dart'; import 'configuration.dart'; diff --git a/lib/src/assets.dart b/lib/src/assets.dart index 9cb8bcc..622f563 100644 --- a/lib/src/assets.dart +++ b/lib/src/assets.dart @@ -10,8 +10,8 @@ import 'package:image/image.dart' drawImage, encodePng, trim; -import 'package:path/path.dart' show basename; -import 'package:cli_util/cli_logging.dart' show Logger; +import 'package:path/path.dart'; +import 'package:cli_util/cli_logging.dart'; import 'configuration.dart'; import 'extensions.dart'; diff --git a/lib/src/configuration.dart b/lib/src/configuration.dart index 9dbdd25..5e2c03e 100644 --- a/lib/src/configuration.dart +++ b/lib/src/configuration.dart @@ -1,11 +1,11 @@ import 'dart:io'; -import 'package:args/args.dart' show ArgParser, ArgResults; -import 'package:cli_util/cli_logging.dart' show Logger; +import 'package:args/args.dart'; +import 'package:cli_util/cli_logging.dart'; import 'package:get_it/get_it.dart'; import 'package:package_config/package_config.dart'; -import 'package:path/path.dart' show extension, basename; +import 'package:path/path.dart'; import 'package:pub_semver/pub_semver.dart'; -import 'package:yaml/yaml.dart' show YamlMap, loadYaml; +import 'package:yaml/yaml.dart'; import 'command_line_converter.dart'; import 'extensions.dart'; diff --git a/lib/src/extensions.dart b/lib/src/extensions.dart index 31f739c..085f7ee 100644 --- a/lib/src/extensions.dart +++ b/lib/src/extensions.dart @@ -1,6 +1,6 @@ import 'dart:io'; -import 'dart:convert' show HtmlEscape; -import 'package:cli_util/cli_logging.dart' show Ansi, Logger; +import 'dart:convert'; +import 'package:cli_util/cli_logging.dart'; import 'package:get_it/get_it.dart'; import 'package:path/path.dart' as path; diff --git a/lib/src/makeappx.dart b/lib/src/makeappx.dart index 84041f4..5c6540f 100644 --- a/lib/src/makeappx.dart +++ b/lib/src/makeappx.dart @@ -1,5 +1,5 @@ import 'dart:io'; -import 'package:cli_util/cli_logging.dart' show Logger; +import 'package:cli_util/cli_logging.dart'; import 'package:get_it/get_it.dart'; import 'package:msix/src/extensions.dart'; import 'configuration.dart'; diff --git a/lib/src/makepri.dart b/lib/src/makepri.dart index 2d61a30..1c295da 100644 --- a/lib/src/makepri.dart +++ b/lib/src/makepri.dart @@ -1,5 +1,5 @@ import 'dart:io'; -import 'package:cli_util/cli_logging.dart' show Logger; +import 'package:cli_util/cli_logging.dart'; import 'package:get_it/get_it.dart'; import 'configuration.dart'; import 'extensions.dart'; diff --git a/lib/src/sign_tool.dart b/lib/src/sign_tool.dart index d724e1d..d78df39 100644 --- a/lib/src/sign_tool.dart +++ b/lib/src/sign_tool.dart @@ -1,9 +1,9 @@ import 'dart:convert'; import 'dart:io'; -import 'package:cli_util/cli_logging.dart' show Logger; +import 'package:cli_util/cli_logging.dart'; import 'package:console/console.dart'; import 'package:get_it/get_it.dart'; -import 'package:path/path.dart' show extension, basename; +import 'package:path/path.dart'; import 'extensions.dart'; import 'configuration.dart'; diff --git a/lib/src/windows_build.dart b/lib/src/windows_build.dart index c8714b0..69b1910 100644 --- a/lib/src/windows_build.dart +++ b/lib/src/windows_build.dart @@ -1,5 +1,4 @@ import 'dart:io'; -import 'package:cli_util/cli_logging.dart' show Logger; import 'package:cli_util/cli_logging.dart'; import 'package:get_it/get_it.dart'; import 'package:msix/src/extensions.dart'; From 08776577704d46dec10dcbdbaad7a25efa44ec54 Mon Sep 17 00:00:00 2001 From: Yehuda Kremer Date: Tue, 1 Nov 2022 08:39:03 +0200 Subject: [PATCH 07/13] switch `U+2013` with `U+002d` --- lib/src/sign_tool.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/sign_tool.dart b/lib/src/sign_tool.dart index d78df39..e6fff6d 100644 --- a/lib/src/sign_tool.dart +++ b/lib/src/sign_tool.dart @@ -109,7 +109,7 @@ class SignTool { String subjectValue = _getSignToolOptionsArgumentValue('/n'); String subject = await _getInstalledCertificateSubject( - "\$_.Subject –like \"*$subjectValue*\""); + "\$_.Subject -like \"*$subjectValue*\""); return subject; } @@ -119,7 +119,7 @@ class SignTool { String subjectValue = _getSignToolOptionsArgumentValue('/i'); String subject = await _getInstalledCertificateSubject( - "\$_.Issuer –like \"*$subjectValue*\""); + "\$_.Issuer -like \"*$subjectValue*\""); return subject; } From 7d32b7daa56f5f8959cd1745c4b95d64cd6151b2 Mon Sep 17 00:00:00 2001 From: Yehuda Kremer Date: Tue, 1 Nov 2022 09:05:30 +0200 Subject: [PATCH 08/13] small refactoring --- lib/msix.dart | 3 +- lib/src/app_installer.dart | 41 ++++++++----------- lib/src/appx_manifest.dart | 2 +- lib/src/assets.dart | 12 +----- lib/src/configuration.dart | 2 +- lib/src/makeappx.dart | 10 ++--- lib/src/makepri.dart | 10 ++--- ...extensions.dart => method_extensions.dart} | 0 lib/src/sign_tool.dart | 29 ++++++------- lib/src/windows_build.dart | 10 ++--- test/appx_manifest_test.dart | 3 -- test/assets_test.dart | 4 -- test/configuration_test.dart | 3 -- 13 files changed, 48 insertions(+), 81 deletions(-) rename lib/src/{extensions.dart => method_extensions.dart} (100%) diff --git a/lib/msix.dart b/lib/msix.dart index 107285d..47100a6 100644 --- a/lib/msix.dart +++ b/lib/msix.dart @@ -1,5 +1,4 @@ import 'dart:io'; - import 'package:cli_util/cli_logging.dart'; import 'package:get_it/get_it.dart'; import 'src/app_installer.dart'; @@ -10,7 +9,7 @@ import 'src/makepri.dart'; import 'src/appx_manifest.dart'; import 'src/makeappx.dart'; import 'src/sign_tool.dart'; -import 'src/extensions.dart'; +import 'src/method_extensions.dart'; /// Main class that handles all the msix package functionality class Msix { diff --git a/lib/src/app_installer.dart b/lib/src/app_installer.dart index 498070b..9e8c898 100644 --- a/lib/src/app_installer.dart +++ b/lib/src/app_installer.dart @@ -3,7 +3,7 @@ import 'dart:convert'; import 'package:console/console.dart'; import 'package:get_it/get_it.dart'; import 'package:image/image.dart'; -import 'package:msix/src/extensions.dart'; +import 'method_extensions.dart'; import 'package:path/path.dart'; import 'package:pub_semver/pub_semver.dart'; import 'package:cli_util/cli_logging.dart'; @@ -98,26 +98,19 @@ class AppInstaller { Future generateAppInstallerWebSite() async { _logger.trace('generate app installer web site'); - webInstallerSite = webInstallerSite.replaceAll( - 'PAGE_TITLE', _config.displayName ?? _config.appName!); - webInstallerSite = webInstallerSite.replaceAll( - 'PAGE_DESCRIPTION', - _config.appDescription ?? - '${_config.displayName ?? _config.appName!} installer'); - webInstallerSite = webInstallerSite.replaceAll( - 'PAGE_TITLE', _config.displayName ?? _config.appName!); - webInstallerSite = webInstallerSite.replaceAll( - 'APP_NAME', _config.displayName ?? _config.appName!); - webInstallerSite = - webInstallerSite.replaceAll('APP_VERSION', _config.msixVersion!); - webInstallerSite = webInstallerSite.replaceAll( - 'APP_INSTALLER_LINK', basename(_config.appInstallerPath)); - webInstallerSite = webInstallerSite.replaceAll( - 'REQUIRED_OS_VERSION', _config.osMinVersion); - webInstallerSite = - webInstallerSite.replaceAll('ARCHITECTURE', _config.architecture!); - webInstallerSite = - webInstallerSite.replaceAll('PUBLISHER_NAME', _config.publisherName!); + webInstallerSite = webInstallerSite + .replaceAll('PAGE_TITLE', _config.displayName ?? _config.appName!) + .replaceAll( + 'PAGE_DESCRIPTION', + _config.appDescription ?? + '${_config.displayName ?? _config.appName!} installer') + .replaceAll('PAGE_TITLE', _config.displayName ?? _config.appName!) + .replaceAll('APP_NAME', _config.displayName ?? _config.appName!) + .replaceAll('APP_VERSION', _config.msixVersion!) + .replaceAll('APP_INSTALLER_LINK', basename(_config.appInstallerPath)) + .replaceAll('REQUIRED_OS_VERSION', _config.osMinVersion) + .replaceAll('ARCHITECTURE', _config.architecture!) + .replaceAll('PUBLISHER_NAME', _config.publisherName!); Image logoImage = decodeImage(await File(_config.logoPath ?? '${_config.defaultsIconsFolderPath}/Square44x44Logo.altform-lightunplated_targetsize-256.png') @@ -133,9 +126,9 @@ class AppInstaller { String base64Logo = base64Encode(encodePng(siteLogo)); String base64favicon = base64Encode(encodePng(favicon)); - webInstallerSite = webInstallerSite.replaceAll('IMAGE_BASE64', base64Logo); - webInstallerSite = - webInstallerSite.replaceAll('FAVICON_BASE64', base64favicon); + webInstallerSite = webInstallerSite + .replaceAll('IMAGE_BASE64', base64Logo) + .replaceAll('FAVICON_BASE64', base64favicon); await File('${_config.publishFolderPath}/index.html') .writeAsString(webInstallerSite); diff --git a/lib/src/appx_manifest.dart b/lib/src/appx_manifest.dart index 22abec0..ebedde2 100644 --- a/lib/src/appx_manifest.dart +++ b/lib/src/appx_manifest.dart @@ -3,7 +3,7 @@ import 'package:cli_util/cli_logging.dart'; import 'package:get_it/get_it.dart'; import 'capabilities.dart'; import 'configuration.dart'; -import 'extensions.dart'; +import 'method_extensions.dart'; /// Handles the creation of the manifest file class AppxManifest { diff --git a/lib/src/assets.dart b/lib/src/assets.dart index 622f563..e72c02b 100644 --- a/lib/src/assets.dart +++ b/lib/src/assets.dart @@ -1,19 +1,11 @@ import 'dart:io'; import 'dart:isolate'; import 'package:get_it/get_it.dart'; -import 'package:image/image.dart' - show - Image, - Interpolation, - copyResize, - decodeImage, - drawImage, - encodePng, - trim; +import 'package:image/image.dart'; import 'package:path/path.dart'; import 'package:cli_util/cli_logging.dart'; import 'configuration.dart'; -import 'extensions.dart'; +import 'method_extensions.dart'; /// Handles all the msix and user assets files class Assets { diff --git a/lib/src/configuration.dart b/lib/src/configuration.dart index 5e2c03e..f8024f2 100644 --- a/lib/src/configuration.dart +++ b/lib/src/configuration.dart @@ -7,7 +7,7 @@ import 'package:path/path.dart'; import 'package:pub_semver/pub_semver.dart'; import 'package:yaml/yaml.dart'; import 'command_line_converter.dart'; -import 'extensions.dart'; +import 'method_extensions.dart'; /// Handles loading and validating the configuration values class Configuration { diff --git a/lib/src/makeappx.dart b/lib/src/makeappx.dart index 5c6540f..c98a6f1 100644 --- a/lib/src/makeappx.dart +++ b/lib/src/makeappx.dart @@ -1,7 +1,7 @@ import 'dart:io'; import 'package:cli_util/cli_logging.dart'; import 'package:get_it/get_it.dart'; -import 'package:msix/src/extensions.dart'; +import 'method_extensions.dart'; import 'configuration.dart'; /// Use the makeappx.exe tool to generate manifest file @@ -15,7 +15,8 @@ class MakeAppx { String makeAppxPath = '${_config.msixToolkitPath}/Redist.${_config.architecture}/makeappx.exe'; - ProcessResult makeAppxProcess = await Process.run(makeAppxPath, [ + // ignore: avoid_single_cascade_in_expression_statements + await Process.run(makeAppxPath, [ 'pack', '/v', '/o', @@ -23,8 +24,7 @@ class MakeAppx { _config.buildFilesFolder, '/p', _config.msixPath - ]); - - makeAppxProcess.exitOnError(); + ]) + ..exitOnError(); } } diff --git a/lib/src/makepri.dart b/lib/src/makepri.dart index 1c295da..ed84135 100644 --- a/lib/src/makepri.dart +++ b/lib/src/makepri.dart @@ -2,7 +2,7 @@ import 'dart:io'; import 'package:cli_util/cli_logging.dart'; import 'package:get_it/get_it.dart'; import 'configuration.dart'; -import 'extensions.dart'; +import 'method_extensions.dart'; /// Use the makepri.exe tool to generate package resource indexing files class MakePri { @@ -16,16 +16,16 @@ class MakePri { String makePriPath = '${_config.msixToolkitPath}/Redist.${_config.architecture}/makepri.exe'; - ProcessResult makePriConfigProcess = await Process.run(makePriPath, [ + // ignore: avoid_single_cascade_in_expression_statements + await Process.run(makePriPath, [ 'createconfig', '/cf', '$buildPath\\priconfig.xml', '/dq', 'en-US', '/o' - ]); - - makePriConfigProcess.exitOnError(); + ]) + ..exitOnError(); ProcessResult makePriProcess = await Process.run(makePriPath, [ 'new', diff --git a/lib/src/extensions.dart b/lib/src/method_extensions.dart similarity index 100% rename from lib/src/extensions.dart rename to lib/src/method_extensions.dart diff --git a/lib/src/sign_tool.dart b/lib/src/sign_tool.dart index e6fff6d..40568d3 100644 --- a/lib/src/sign_tool.dart +++ b/lib/src/sign_tool.dart @@ -4,7 +4,7 @@ import 'package:cli_util/cli_logging.dart'; import 'package:console/console.dart'; import 'package:get_it/get_it.dart'; import 'package:path/path.dart'; -import 'extensions.dart'; +import 'method_extensions.dart'; import 'configuration.dart'; RegExp _publisherRegex = RegExp( @@ -75,15 +75,11 @@ class SignTool { } } - Future _executePowershellCommand(String command) async { - ProcessResult processResult = await Process.run( - 'powershell.exe', ['-NoProfile', '-NonInteractive', command], - stdoutEncoding: utf8, stderrEncoding: utf8); - - processResult.exitOnError(); - - return processResult; - } + Future _executePowershellCommand(String command) async => + await Process.run( + 'powershell.exe', ['-NoProfile', '-NonInteractive', command], + stdoutEncoding: utf8, stderrEncoding: utf8) + ..exitOnError(); Future _getInstalledCertificateSubject(String searchCondition) async { ProcessResult certificateDetailsProcess = await _executePowershellCommand( @@ -144,9 +140,8 @@ class SignTool { '-NoProfile', '-NonInteractive', "dir Cert:\\CurrentUser\\Root | Where-Object { \$_.Subject -eq '${_config.publisher}'}" - ]); - - getInstalledCertificate.exitOnError(); + ]) + ..exitOnError(); bool isCertificateNotInstalled = getInstalledCertificate.stdout.toString().isNullOrEmpty; @@ -231,13 +226,13 @@ class SignTool { bool isFullSigntoolCommand = signtoolOptions[0].toLowerCase().contains('signtool'); - ProcessResult signProcess = await Process.run( + // ignore: avoid_single_cascade_in_expression_statements + await Process.run( isFullSigntoolCommand ? signtoolOptions[0] : signtoolPath, [ if (!isFullSigntoolCommand) 'sign', ...signtoolOptions.skip(isFullSigntoolCommand ? 1 : 0), _config.msixPath, - ]); - - signProcess.exitOnError(); + ]) + ..exitOnError(); } } diff --git a/lib/src/windows_build.dart b/lib/src/windows_build.dart index 69b1910..0a577b7 100644 --- a/lib/src/windows_build.dart +++ b/lib/src/windows_build.dart @@ -1,8 +1,7 @@ import 'dart:io'; import 'package:cli_util/cli_logging.dart'; import 'package:get_it/get_it.dart'; -import 'package:msix/src/extensions.dart'; - +import 'method_extensions.dart'; import 'configuration.dart'; const runnerRcPath = 'windows/runner/Runner.rc'; @@ -21,10 +20,9 @@ class WindowsBuild { Progress loggerProgress = _logger .progress('running "flutter ${buildWindowsArguments.join(' ')}"'); - ProcessResult windowsBuildProcess = - await Process.run('flutter', buildWindowsArguments, runInShell: true); - - windowsBuildProcess.exitOnError(); + // ignore: avoid_single_cascade_in_expression_statements + await Process.run('flutter', buildWindowsArguments, runInShell: true) + ..exitOnError(); loggerProgress.finish(showTiming: true); } diff --git a/test/appx_manifest_test.dart b/test/appx_manifest_test.dart index 25d65e4..64fe7de 100644 --- a/test/appx_manifest_test.dart +++ b/test/appx_manifest_test.dart @@ -36,16 +36,13 @@ void main() { GetIt.I.registerSingleton(config); await Directory('$tempFolderPath/').create(recursive: true); - await Future.delayed(const Duration(milliseconds: 150)); }); tearDown(() async { GetIt.I.reset(); if (await Directory('$tempFolderPath/').exists()) { - await Future.delayed(const Duration(milliseconds: 150)); await Directory('$tempFolderPath/').delete(recursive: true); - await Future.delayed(const Duration(milliseconds: 150)); } }); diff --git a/test/assets_test.dart b/test/assets_test.dart index 073bdf9..a5d65cb 100644 --- a/test/assets_test.dart +++ b/test/assets_test.dart @@ -34,17 +34,13 @@ void main() { GetIt.I.registerSingleton(config); await Directory('$tempFolderPath/').create(recursive: true); - await Future.delayed(const Duration(milliseconds: 150)); }); tearDown(() async { GetIt.I.reset(); if (await Directory('$tempFolderPath/').exists()) { - await Future.delayed(const Duration(milliseconds: 150)); - await Future.delayed(const Duration(milliseconds: 150)); await Directory('$tempFolderPath/').delete(recursive: true); - await Future.delayed(const Duration(milliseconds: 150)); } }); test('copy defaults icons', () async { diff --git a/test/configuration_test.dart b/test/configuration_test.dart index 07b6d41..d35f62b 100644 --- a/test/configuration_test.dart +++ b/test/configuration_test.dart @@ -26,16 +26,13 @@ msix_config: GetIt.I.registerSingleton(config); await Directory('$tempFolderPath/').create(recursive: true); - await Future.delayed(const Duration(milliseconds: 150)); }); tearDown(() async { GetIt.I.reset(); if (await Directory('$tempFolderPath/').exists()) { - await Future.delayed(const Duration(milliseconds: 150)); await Directory('$tempFolderPath/').delete(recursive: true); - await Future.delayed(const Duration(milliseconds: 150)); } }); From 6db744daf4fc6deb12c9eebb456e48d1194db80b Mon Sep 17 00:00:00 2001 From: Yehuda Kremer Date: Tue, 1 Nov 2022 10:29:23 +0200 Subject: [PATCH 09/13] improving icons-generate performance by 75% --- lib/src/assets.dart | 77 +++++++++++++++++++++++++++++++-------------- 1 file changed, 54 insertions(+), 23 deletions(-) diff --git a/lib/src/assets.dart b/lib/src/assets.dart index e72c02b..290b3a3 100644 --- a/lib/src/assets.dart +++ b/lib/src/assets.dart @@ -20,19 +20,48 @@ class Assets { await Directory(_msixIconsFolderPath).create(); - final port = ReceivePort(); - await Isolate.spawn( - _config.logoPath != null ? _generateAssetsIcons : _copyDefaultsIcons, - port.sendPort); - await port.first; - } + if (_config.logoPath != null) { + _logger.trace('generating icons'); - Future _copyDefaultsIcons(SendPort port) async { - await Directory(_config.defaultsIconsFolderPath) - .copyDirectory(Directory(_msixIconsFolderPath)); - Isolate.exit(port); + if (!(await File(_config.logoPath!).exists())) { + throw 'Logo file not found at ${_config.logoPath}'; + } + + try { + image = decodeImage(await File(_config.logoPath!).readAsBytes())!; + } catch (e) { + throw 'Error reading logo file: ${_config.logoPath!}'; + } + + var generateAssetsIconsParts = [ + _generateAssetsIconsPart1, + _generateAssetsIconsPart2, + _generateAssetsIconsPart3, + ]; + + var isolatesFutures = [ + ReceivePort(), + ReceivePort(), + ReceivePort(), + ] + .asMap() + .map((index, port) => MapEntry(index, [ + Isolate.spawn(generateAssetsIconsParts[index], port.sendPort), + port.first + ])) + .values + .expand((i) => i); + + await Future.wait(isolatesFutures); + } else { + await _copyDefaultsIcons(); + } } + Future _copyDefaultsIcons() async => + await Directory(_config.defaultsIconsFolderPath) + .copyDirectory(Directory(_msixIconsFolderPath)); + /// Copy the VC libs files (msvcp140.dll, vcruntime140.dll, vcruntime140_1.dll) Future copyVCLibsFiles() async { _logger.trace('copying VC libraries'); @@ -140,19 +169,7 @@ class Assets { } /// Generate optimized msix icons from the user logo - Future _generateAssetsIcons(SendPort port) async { - _logger.trace('generating icons'); - - if (!(await File(_config.logoPath!).exists())) { - throw 'Logo file not found at ${_config.logoPath}'; - } - - try { - image = decodeImage(await File(_config.logoPath!).readAsBytes())!; - } catch (e) { - throw 'Error reading logo file: ${_config.logoPath!}'; - } - + Future _generateAssetsIconsPart1(SendPort port) async { await Future.wait([ // SmallTile _generateIcon('SmallTile', const _Size(71, 71), @@ -209,6 +226,13 @@ class Assets { paddingWidthPercent: 0.16, paddingHeightPercent: 0.16, scale: 2), _generateIcon('Square44x44Logo', const _Size(44, 44), paddingWidthPercent: 0.16, paddingHeightPercent: 0.16, scale: 4), + ]); + + Isolate.exit(port); + } + + Future _generateAssetsIconsPart2(SendPort port) async { + await Future.wait([ // targetsize _generateIcon('Square44x44Logo.targetsize-16', const _Size(16, 16)), _generateIcon('Square44x44Logo.targetsize-24', const _Size(24, 24)), @@ -253,6 +277,13 @@ class Assets { const _Size(80, 80)), _generateIcon('Square44x44Logo.altform-unplated_targetsize-96', const _Size(96, 96)), + ]); + + Isolate.exit(port); + } + + Future _generateAssetsIconsPart3(SendPort port) async { + await Future.wait([ // light unplated targetsize _generateIcon('Square44x44Logo.altform-lightunplated_targetsize-16', const _Size(16, 16)), From b252a98818abbd2b2341525aa283c2e9e02af00b Mon Sep 17 00:00:00 2001 From: Yehuda Kremer Date: Tue, 1 Nov 2022 11:42:28 +0200 Subject: [PATCH 10/13] get certificate Subject with X509Certificate2 --- lib/src/sign_tool.dart | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/lib/src/sign_tool.dart b/lib/src/sign_tool.dart index 40568d3..6541a42 100644 --- a/lib/src/sign_tool.dart +++ b/lib/src/sign_tool.dart @@ -28,10 +28,12 @@ class SignTool { subject = await _getCertificateSubjectBySubject(); } else if (_config.signToolOptions!.containsArgument('/i')) { subject = await _getCertificateSubjectByIssuer(); + } else if (_config.signToolOptions!.containsArgument('/f')) { + subject = await _getCertificateSubjectByFile(true); } } else if (_config.certificatePath != null && - extension(_config.certificatePath!).toLowerCase() == '.pfx') { - subject = await _getPfxCertificateSubject(); + _config.certificatePath!.isNotEmpty) { + subject = await _getCertificateSubjectByFile(false); } if (subject.isNotEmpty) { @@ -120,12 +122,20 @@ class SignTool { return subject; } - Future _getPfxCertificateSubject() async { - _logger.trace('getting pfx certificate Subject'); + Future _getCertificateSubjectByFile(bool fromSignToolOptions) async { + _logger.trace('getting certificate "Subject" by file certificate'); + String filePathValue = fromSignToolOptions + ? _getSignToolOptionsArgumentValue('/f') + : _config.certificatePath!; + var passwordValue = _config.certificatePassword ?? ''; + if (fromSignToolOptions && + _config.signToolOptions!.containsArgument('/p')) { + passwordValue = _getSignToolOptionsArgumentValue('/p'); + } ProcessResult certificateDetailsProcess = await _executePowershellCommand( - """new-object System.Security.Cryptography.X509Certificates.X509Certificate2("${_config.certificatePath}", - "${_config.certificatePassword}") | select -expandproperty Subject -First 1"""); + """new-object System.Security.Cryptography.X509Certificates.X509Certificate2("$filePathValue", + "$passwordValue") | select -expandproperty Subject -First 1"""); String subject = (certificateDetailsProcess.stdout as String).trim(); From 7b95857411072d64cb014d01af9944a5669a2fa9 Mon Sep 17 00:00:00 2001 From: Yehuda Kremer Date: Tue, 1 Nov 2022 11:43:33 +0200 Subject: [PATCH 11/13] fix variable name --- lib/src/sign_tool.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/sign_tool.dart b/lib/src/sign_tool.dart index 6541a42..95f4cca 100644 --- a/lib/src/sign_tool.dart +++ b/lib/src/sign_tool.dart @@ -115,9 +115,9 @@ class SignTool { Future _getCertificateSubjectByIssuer() async { _logger.trace('getting certificate "Subject" by certificate Issuer'); - String subjectValue = _getSignToolOptionsArgumentValue('/i'); + String issuerValue = _getSignToolOptionsArgumentValue('/i'); String subject = await _getInstalledCertificateSubject( - "\$_.Issuer -like \"*$subjectValue*\""); + "\$_.Issuer -like \"*$issuerValue*\""); return subject; } From bc0d17ef64e3d35e9018a0d085e23b9ad9368974 Mon Sep 17 00:00:00 2001 From: Yehuda Kremer Date: Tue, 1 Nov 2022 11:49:42 +0200 Subject: [PATCH 12/13] update documentation --- README.md | 16 ++++++++-------- example/README.md | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 666d3d0..cb250f4 100644 --- a/README.md +++ b/README.md @@ -98,14 +98,14 @@ See [Configurations Examples And Use Cases].
Sign configuration (click to expand) -| YAML name | Command-line argument | Description | Example | -| ---------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | -| `certificate_path` | `--certificate-path` `-c` | Path to the certificate content to place in the store. | `C:\certs\signcert.pfx` | -| `certificate_password` | `--certificate-password` `-p` | Password for the certificate. | `1234` | -| `publisher` | `--publisher` `-b` | The `Subject` value in the certificate.
Required only if publish to the store, or if the `Publisher` not found by this package. | `CN=BF212345-5644-46DF-8668-014043C1B138` or `CN=Contoso Software, O=Contoso Corporation, C=US` | -| `signtool_options` | `--signtool-options` | Options to be provided to the `signtool` for app signing (see below.) | `/v /fd SHA256 /f C:/Users/me/Desktop/my.cer` | -| `sign_msix` | `--sign-msix ` | If `false`, don't sign the msix file, default is `true`.
Note: when **false**, `publisher` is Required. | `true` | -| `install_certificate` | `--install-certificate ` | If `false`, don't try to install the certificate, default is `true`. | `true` | +| YAML name | Command-line argument | Description | Example | +| ---------------------- | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | +| `certificate_path` | `--certificate-path` `-c` | Path to the certificate content to place in the store. | `C:\certs\signcert.pfx` or `C:\certs\signcert.crt` | +| `certificate_password` | `--certificate-password` `-p` | Password for the certificate. | `1234` | +| `publisher` | `--publisher` `-b` | The `Subject` value in the certificate.
Required only if publish to the store, or if the `Publisher` will not found automatically by this package. | `CN=BF212345-5644-46DF-8668-014043C1B138` or `CN=Contoso Software, O=Contoso Corporation, C=US` | +| `signtool_options` | `--signtool-options` | Options to be provided to the `signtool` for app signing (see below.) | `/v /fd SHA256 /f C:/Users/me/Desktop/my.cer` | +| `sign_msix` | `--sign-msix ` | If `false`, don't sign the msix file, default is `true`.
Note: when **false**, `publisher` is Required. | `true` | +| `install_certificate` | `--install-certificate ` | If `false`, don't try to install the certificate, default is `true`. | `true` |
diff --git a/example/README.md b/example/README.md index 80d6e41..c8666e4 100644 --- a/example/README.md +++ b/example/README.md @@ -96,3 +96,21 @@ msix_config: msix_version: 1.0.0.0 signtool_options: /fd sha256 /sha1 028bc9922d198ee83d776aa19cb8e82897691e0c /tr http://timestamp.digicert.com ``` + +###### By .crt File: + +```yaml +msix_config: + display_name: Flutter App + msix_version: 1.0.0.0 + signtool_options: /fd SHA256 /f "\test_certificate.crt" +``` + +###### By .pfx File: + +```yaml +msix_config: + display_name: Flutter App + msix_version: 1.0.0.0 + signtool_options: /fd SHA256 /f "\test_certificate.pfx" /p 1234 +``` From 6220f2677cdab1396c56b7d4dbc4b339818980ca Mon Sep 17 00:00:00 2001 From: Yehuda Kremer Date: Tue, 1 Nov 2022 11:58:06 +0200 Subject: [PATCH 13/13] change to explicit type annotation --- lib/msix.dart | 6 +++--- lib/src/assets.dart | 2 +- lib/src/command_line_converter.dart | 2 +- lib/src/configuration.dart | 4 ++-- lib/src/makepri.dart | 2 +- lib/src/sign_tool.dart | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/msix.dart b/lib/msix.dart index 47100a6..eaaed71 100644 --- a/lib/msix.dart +++ b/lib/msix.dart @@ -26,7 +26,7 @@ class Msix { Future build() async { await _initConfig(); await _buildMsixFiles(); - final msixStyledPath = File(_msixOutputPath).parent.path.blue.emphasized; + String msixStyledPath = File(_msixOutputPath).parent.path.blue.emphasized; _logger .write('${'unpackaged msix files created in: '.green}$msixStyledPath'); } @@ -105,7 +105,7 @@ class Msix { Progress loggerProgress = _logger.progress('building msix files'); await _config.validateWindowsBuildFiles(); - final assets = Assets(); + Assets assets = Assets(); await assets.cleanTemporaryFiles(clearMsixFiles: true); await assets.createIcons(); await assets.copyVCLibsFiles(); @@ -126,7 +126,7 @@ class Msix { await Assets().cleanTemporaryFiles(); if (_config.signMsix && !_config.store) { - final signTool = SignTool(); + SignTool signTool = SignTool(); if (_config.installCert && (_config.signToolOptions == null || diff --git a/lib/src/assets.dart b/lib/src/assets.dart index 290b3a3..6f86d4b 100644 --- a/lib/src/assets.dart +++ b/lib/src/assets.dart @@ -39,7 +39,7 @@ class Assets { _generateAssetsIconsPart3, ]; - var isolatesFutures = [ + Iterable isolatesFutures = [ ReceivePort(), ReceivePort(), ReceivePort(), diff --git a/lib/src/command_line_converter.dart b/lib/src/command_line_converter.dart index ff42ba4..131556d 100644 --- a/lib/src/command_line_converter.dart +++ b/lib/src/command_line_converter.dart @@ -19,7 +19,7 @@ class CommandLineConverter extends Converter> { bool lastTokenHasBeenQuoted = false; for (int index = 0; index < input.length; index++) { - final token = input[index]; + final String token = input[index]; if (inQuote != null) { if (token == inQuote) { diff --git a/lib/src/configuration.dart b/lib/src/configuration.dart index f8024f2..64e9e72 100644 --- a/lib/src/configuration.dart +++ b/lib/src/configuration.dart @@ -110,7 +110,7 @@ class Configuration { publisher = _args['publisher'] ?? yaml['publisher']; identityName = _args['identity-name'] ?? yaml['identity_name']; logoPath = _args['logo-path'] ?? yaml['logo_path']; - final signToolOptionsConfig = + final String? signToolOptionsConfig = (_args['signtool-options'] ?? yaml['signtool_options'])?.toString(); if (signToolOptionsConfig != null && signToolOptionsConfig.isNotEmpty) { CommandLineConverter commandLineConverter = CommandLineConverter(); @@ -352,7 +352,7 @@ class Configuration { // Existing behavior is to put null if no version, so matching if (yaml['version'] == null) return null; try { - final pubspecVersion = Version.parse(yaml['version']); + final Version pubspecVersion = Version.parse(yaml['version']); return [ pubspecVersion.major, pubspecVersion.minor, diff --git a/lib/src/makepri.dart b/lib/src/makepri.dart index ed84135..1ba9884 100644 --- a/lib/src/makepri.dart +++ b/lib/src/makepri.dart @@ -12,7 +12,7 @@ class MakePri { Future generatePRI() async { _logger.trace('generate package resource indexing files'); - final buildPath = _config.buildFilesFolder; + final String buildPath = _config.buildFilesFolder; String makePriPath = '${_config.msixToolkitPath}/Redist.${_config.architecture}/makepri.exe'; diff --git a/lib/src/sign_tool.dart b/lib/src/sign_tool.dart index 95f4cca..400fa03 100644 --- a/lib/src/sign_tool.dart +++ b/lib/src/sign_tool.dart @@ -128,7 +128,7 @@ class SignTool { String filePathValue = fromSignToolOptions ? _getSignToolOptionsArgumentValue('/f') : _config.certificatePath!; - var passwordValue = _config.certificatePassword ?? ''; + String passwordValue = _config.certificatePassword ?? ''; if (fromSignToolOptions && _config.signToolOptions!.containsArgument('/p')) { passwordValue = _getSignToolOptionsArgumentValue('/p');