Create open Shorebird workspace meta-repo
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
import 'package:license_flavor_patch_test/license.dart';
|
||||
|
||||
void main() {
|
||||
final license = currentLicenseType();
|
||||
print('license:${license.name}');
|
||||
print(proFeatureEnabled ? 'pro-feature:enabled' : 'pro-feature:off');
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
enum LicenseType { free, pro, enterprise }
|
||||
|
||||
LicenseType currentLicenseType() {
|
||||
const license = String.fromEnvironment('LICENSE_TYPE', defaultValue: 'free');
|
||||
return switch (license) {
|
||||
'pro' => LicenseType.pro,
|
||||
'enterprise' => LicenseType.enterprise,
|
||||
_ => LicenseType.free,
|
||||
};
|
||||
}
|
||||
|
||||
bool get proFeatureEnabled =>
|
||||
currentLicenseType() == LicenseType.pro ||
|
||||
currentLicenseType() == LicenseType.enterprise;
|
||||
@@ -0,0 +1,34 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'license.dart';
|
||||
|
||||
void main() => runApp(const LicenseFlavorPatchApp());
|
||||
|
||||
class LicenseFlavorPatchApp extends StatelessWidget {
|
||||
const LicenseFlavorPatchApp({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final license = currentLicenseType();
|
||||
return MaterialApp(
|
||||
home: Scaffold(
|
||||
appBar: AppBar(title: const Text('License Patch Test')),
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
'license:${license.name}',
|
||||
key: const ValueKey('license-status'),
|
||||
),
|
||||
Text(
|
||||
proFeatureEnabled ? 'pro-feature:enabled' : 'pro-feature:off',
|
||||
key: const ValueKey('feature-status'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.13.1"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.1"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: clock
|
||||
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.19.1"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fake_async
|
||||
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.3"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "11.0.2"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.10"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.19"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.13.0"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.17.0"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.2"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.12.1"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.1"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.2"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.10"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: "0016aef94fc66495ac78af5859181e3f3bf2026bd8eecc72b9565601e19ab360"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "15.2.0"
|
||||
sdks:
|
||||
dart: ">=3.9.0 <4.0.0"
|
||||
flutter: ">=3.18.0-18.0.pre.54"
|
||||
@@ -0,0 +1,19 @@
|
||||
name: license_flavor_patch_test
|
||||
description: Test app for encrypted flavor/license AOT patch verification.
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
sdk: ^3.9.0
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
assets:
|
||||
- shorebird.yaml
|
||||
@@ -0,0 +1,5 @@
|
||||
app_id: license-flavor-patch-test
|
||||
flavors:
|
||||
free: license-flavor-free
|
||||
pro: license-flavor-pro
|
||||
enterprise: license-flavor-enterprise
|
||||
@@ -0,0 +1,19 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:license_flavor_patch_test/license.dart';
|
||||
import 'package:license_flavor_patch_test/main.dart';
|
||||
|
||||
void main() {
|
||||
final isProFlavor = currentLicenseType() == LicenseType.pro;
|
||||
|
||||
test('pro flavor enables pro feature gate', () {
|
||||
expect(currentLicenseType(), LicenseType.pro);
|
||||
expect(proFeatureEnabled, isTrue);
|
||||
}, skip: !isProFlavor);
|
||||
|
||||
testWidgets('renders pro license status labels', (tester) async {
|
||||
await tester.pumpWidget(const LicenseFlavorPatchApp());
|
||||
|
||||
expect(find.text('license:pro'), findsOneWidget);
|
||||
expect(find.text('pro-feature:enabled'), findsOneWidget);
|
||||
}, skip: !isProFlavor);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:license_flavor_patch_test/license.dart';
|
||||
import 'package:license_flavor_patch_test/main.dart';
|
||||
|
||||
void main() {
|
||||
test('defaults to free license flavor', () {
|
||||
expect(currentLicenseType(), LicenseType.free);
|
||||
expect(proFeatureEnabled, isFalse);
|
||||
});
|
||||
|
||||
testWidgets('renders free license status labels', (tester) async {
|
||||
await tester.pumpWidget(const LicenseFlavorPatchApp());
|
||||
|
||||
expect(find.text('license:free'), findsOneWidget);
|
||||
expect(find.text('pro-feature:off'), findsOneWidget);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,363 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
Future<void> main() async {
|
||||
final appDir = File.fromUri(Platform.script).parent.parent;
|
||||
final workspaceRoot = appDir.parent.parent;
|
||||
final dartRoot = Directory('${workspaceRoot.path}/dart-sdk-new');
|
||||
final patchBuildDir = Directory('${dartRoot.path}/out/ReleaseX64AotPatch');
|
||||
final toolWorkspace = Directory('${workspaceRoot.path}/shorebird');
|
||||
final workDir = Directory('${appDir.path}/build/open_aot_patch_verify');
|
||||
|
||||
final argsFile = File('${patchBuildDir.path}/args.gn');
|
||||
if (!argsFile.existsSync()) {
|
||||
throw StateError(
|
||||
'Missing ${argsFile.path}. Build the patch runtime first with '
|
||||
'dart_enable_aot_patching=true and dart_dynamic_modules=false.',
|
||||
);
|
||||
}
|
||||
final args = argsFile.readAsStringSync();
|
||||
if (!args.contains('dart_enable_aot_patching = true') ||
|
||||
!args.contains('dart_dynamic_modules = false')) {
|
||||
throw StateError(
|
||||
'${argsFile.path} must enable AOT patching and disable dynamic modules.',
|
||||
);
|
||||
}
|
||||
|
||||
if (workDir.existsSync()) {
|
||||
workDir.deleteSync(recursive: true);
|
||||
}
|
||||
workDir.createSync(recursive: true);
|
||||
|
||||
final packageConfig = File('${workDir.path}/package_config.json');
|
||||
packageConfig.writeAsStringSync(
|
||||
jsonEncode({
|
||||
'configVersion': 2,
|
||||
'packages': [
|
||||
{
|
||||
'name': 'license_flavor_patch_test',
|
||||
'rootUri': appDir.uri.toString(),
|
||||
'packageUri': 'lib/',
|
||||
'languageVersion': '3.9',
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
final baseMap = File('${workDir.path}/base.obfuscation.json');
|
||||
final patchMap = File('${workDir.path}/patch.obfuscation.json');
|
||||
final baseVmcode = File('${workDir.path}/base_free.vmcode');
|
||||
final patchVmcode = File('${workDir.path}/patch_pro.vmcode');
|
||||
final reconstructedVmcode = File('${workDir.path}/reconstructed_pro.vmcode');
|
||||
|
||||
await _compileAot(
|
||||
workspaceRoot: workspaceRoot,
|
||||
dartRoot: dartRoot,
|
||||
patchBuildDir: patchBuildDir,
|
||||
packageConfig: packageConfig,
|
||||
licenseType: 'free',
|
||||
outputVmcode: baseVmcode,
|
||||
saveObfuscationMap: baseMap,
|
||||
);
|
||||
await _compileAot(
|
||||
workspaceRoot: workspaceRoot,
|
||||
dartRoot: dartRoot,
|
||||
patchBuildDir: patchBuildDir,
|
||||
packageConfig: packageConfig,
|
||||
licenseType: 'pro',
|
||||
outputVmcode: patchVmcode,
|
||||
loadObfuscationMap: baseMap,
|
||||
saveObfuscationMap: patchMap,
|
||||
);
|
||||
|
||||
_expectStatus(
|
||||
await _runAot(patchBuildDir, baseVmcode),
|
||||
license: 'free',
|
||||
proFeature: false,
|
||||
);
|
||||
_expectStatus(
|
||||
await _runAot(patchBuildDir, patchVmcode),
|
||||
license: 'pro',
|
||||
proFeature: true,
|
||||
);
|
||||
|
||||
final artifact = File('${workDir.path}/patch.json');
|
||||
final encrypted = File('${workDir.path}/patch.encrypted.json');
|
||||
const keyHex =
|
||||
'000102030405060708090a0b0c0d0e0f'
|
||||
'101112131415161718191a1b1c1d1e1f';
|
||||
const wrongKeyHex =
|
||||
'1f1e1d1c1b1a19181716151413121110'
|
||||
'0f0e0d0c0b0a09080706050403020100';
|
||||
const nonceHex = '000102030405060708090a0b';
|
||||
const offlineExpiresAt = '2030-01-01T00:00:00Z';
|
||||
const beforeExpiry = '2029-01-01T00:00:00Z';
|
||||
const afterExpiry = '2031-01-01T00:00:00Z';
|
||||
|
||||
await _runTool(toolWorkspace, [
|
||||
'link',
|
||||
'--base=${baseVmcode.path}',
|
||||
'--patch=${patchVmcode.path}',
|
||||
'--output=${artifact.path}',
|
||||
'--app-id=license-flavor-patch-test',
|
||||
'--app-build-id=host-aot-smoke',
|
||||
'--base-flavor-id=free',
|
||||
'--base-license-type=free',
|
||||
'--flavor-id=pro',
|
||||
'--license-type=pro',
|
||||
'--sdk-hash=${_sha256File(File('${patchBuildDir.path}/gen_snapshot.exe'))}',
|
||||
'--target-os=windows',
|
||||
'--target-arch=x64',
|
||||
'--obfuscation-map-hash=${_sha256File(baseMap)}',
|
||||
'--offline-expires-at=$offlineExpiresAt',
|
||||
'--full-snapshot=true',
|
||||
]);
|
||||
|
||||
final artifactJson = (jsonDecode(artifact.readAsStringSync()) as Map)
|
||||
.cast<String, Object?>();
|
||||
if (artifactJson['payload_kind'] != 'full-snapshot') {
|
||||
throw StateError('Expected a directly loadable full-snapshot payload.');
|
||||
}
|
||||
final metadata = (artifactJson['metadata'] as Map).cast<String, Object?>();
|
||||
if (metadata['offline_expires_at'] != '2030-01-01T00:00:00.000Z') {
|
||||
throw StateError('Expected normalized offline_expires_at metadata.');
|
||||
}
|
||||
|
||||
await _runTool(toolWorkspace, [
|
||||
'encrypt',
|
||||
'--input=${artifact.path}',
|
||||
'--output=${encrypted.path}',
|
||||
'--key-id=test-key',
|
||||
'--key-hex=$keyHex',
|
||||
'--nonce-hex=$nonceHex',
|
||||
]);
|
||||
|
||||
await _runTool(toolWorkspace, [
|
||||
'verify',
|
||||
'--input=${encrypted.path}',
|
||||
'--key-hex=$keyHex',
|
||||
'--artifact-sha256=${_sha256File(encrypted)}',
|
||||
'--base-flavor-id=free',
|
||||
'--base-license-type=free',
|
||||
'--flavor-id=pro',
|
||||
'--license-type=pro',
|
||||
'--base=${baseVmcode.path}',
|
||||
'--now=$beforeExpiry',
|
||||
]);
|
||||
await _runToolExpectFailure(toolWorkspace, [
|
||||
'verify',
|
||||
'--input=${encrypted.path}',
|
||||
'--key-hex=$keyHex',
|
||||
'--base-flavor-id=free',
|
||||
'--base-license-type=free',
|
||||
'--flavor-id=pro',
|
||||
'--license-type=pro',
|
||||
'--base=${baseVmcode.path}',
|
||||
'--now=$afterExpiry',
|
||||
]);
|
||||
await _runToolExpectFailure(toolWorkspace, [
|
||||
'verify',
|
||||
'--input=${encrypted.path}',
|
||||
'--key-hex=$wrongKeyHex',
|
||||
'--base-flavor-id=free',
|
||||
'--base-license-type=free',
|
||||
'--flavor-id=pro',
|
||||
'--license-type=pro',
|
||||
'--base=${baseVmcode.path}',
|
||||
]);
|
||||
await _runToolExpectFailure(toolWorkspace, [
|
||||
'verify',
|
||||
'--input=${encrypted.path}',
|
||||
'--key-hex=$keyHex',
|
||||
'--base-flavor-id=free',
|
||||
'--base-license-type=free',
|
||||
'--flavor-id=enterprise',
|
||||
'--license-type=pro',
|
||||
'--base=${baseVmcode.path}',
|
||||
]);
|
||||
|
||||
await _runTool(toolWorkspace, [
|
||||
'dump-blobs',
|
||||
'--input=${encrypted.path}',
|
||||
'--key-hex=$keyHex',
|
||||
'--base=${baseVmcode.path}',
|
||||
'--output=${reconstructedVmcode.path}',
|
||||
'--now=$beforeExpiry',
|
||||
]);
|
||||
if (_sha256File(reconstructedVmcode) != _sha256File(patchVmcode)) {
|
||||
throw StateError('Reconstructed patch vmcode does not match pro snapshot.');
|
||||
}
|
||||
|
||||
_expectStatus(
|
||||
await _runAot(patchBuildDir, reconstructedVmcode),
|
||||
license: 'pro',
|
||||
proFeature: true,
|
||||
);
|
||||
|
||||
await _runToolExpectFailure(toolWorkspace, [
|
||||
'dump-blobs',
|
||||
'--input=${encrypted.path}',
|
||||
'--key-hex=$keyHex',
|
||||
'--base=${baseVmcode.path}',
|
||||
'--output=${reconstructedVmcode.path}',
|
||||
'--now=$afterExpiry',
|
||||
]);
|
||||
|
||||
stdout.writeln(
|
||||
'AOT patch applied to license_flavor_patch_test successfully.',
|
||||
);
|
||||
stdout.writeln('base: license:free / pro-feature:off');
|
||||
stdout.writeln('patch: license:pro / pro-feature:enabled');
|
||||
stdout.writeln('artifact: ${encrypted.path}');
|
||||
stdout.writeln('vmcode: ${reconstructedVmcode.path}');
|
||||
}
|
||||
|
||||
Future<void> _compileAot({
|
||||
required Directory workspaceRoot,
|
||||
required Directory dartRoot,
|
||||
required Directory patchBuildDir,
|
||||
required File packageConfig,
|
||||
required String licenseType,
|
||||
required File outputVmcode,
|
||||
required File saveObfuscationMap,
|
||||
File? loadObfuscationMap,
|
||||
}) async {
|
||||
final dart = '${dartRoot.path}/tools/sdks/dart-sdk/bin/dart.exe';
|
||||
final genKernel = '${dartRoot.path}/pkg/vm/bin/gen_kernel.dart';
|
||||
final source =
|
||||
'${workspaceRoot.path}/testapps/license_flavor_patch_test/bin/license_status.dart';
|
||||
final dill = File('${outputVmcode.parent.path}/${licenseType}_app.dill');
|
||||
await _run(dart, [
|
||||
genKernel,
|
||||
'--packages',
|
||||
packageConfig.path,
|
||||
'--platform',
|
||||
'${patchBuildDir.path}/vm_platform_product.dill',
|
||||
'--aot',
|
||||
'--target-os',
|
||||
'windows',
|
||||
'-Ddart.vm.product=true',
|
||||
'-DLICENSE_TYPE=$licenseType',
|
||||
'-o',
|
||||
dill.path,
|
||||
'--invocation-modes=compile',
|
||||
'--verbosity=all',
|
||||
source,
|
||||
]);
|
||||
|
||||
final genSnapshot = '${patchBuildDir.path}/gen_snapshot.exe';
|
||||
await _run(genSnapshot, [
|
||||
'--snapshot-kind=app-aot-elf',
|
||||
'--elf=${outputVmcode.path}',
|
||||
'--strip',
|
||||
'--obfuscate',
|
||||
if (loadObfuscationMap != null)
|
||||
'--load-obfuscation-map=${loadObfuscationMap.path}',
|
||||
'--save-obfuscation-map=${saveObfuscationMap.path}',
|
||||
dill.path,
|
||||
]);
|
||||
}
|
||||
|
||||
Future<String> _runAot(Directory patchBuildDir, File vmcode) async {
|
||||
final result = await _run('${patchBuildDir.path}/dartaotruntime.exe', [
|
||||
vmcode.path,
|
||||
]);
|
||||
return result.stdout as String;
|
||||
}
|
||||
|
||||
Future<ProcessResult> _runTool(
|
||||
Directory toolWorkspace,
|
||||
List<String> args,
|
||||
) async {
|
||||
return _run(
|
||||
'${toolWorkspace.parent.path}/dart-sdk-new/tools/sdks/dart-sdk/bin/dart.exe',
|
||||
['packages/open_aot_patch_tools/bin/open_aot_patch_tools.dart', ...args],
|
||||
workingDirectory: toolWorkspace,
|
||||
);
|
||||
}
|
||||
|
||||
Future<ProcessResult> _runToolExpectFailure(
|
||||
Directory toolWorkspace,
|
||||
List<String> args,
|
||||
) async {
|
||||
final result = await Process.run(
|
||||
'${toolWorkspace.parent.path}/dart-sdk-new/tools/sdks/dart-sdk/bin/dart.exe',
|
||||
['packages/open_aot_patch_tools/bin/open_aot_patch_tools.dart', ...args],
|
||||
workingDirectory: toolWorkspace.path,
|
||||
);
|
||||
if (result.exitCode == 0) {
|
||||
throw StateError(
|
||||
'Expected open_aot_patch_tools ${args.join(' ')} to fail.',
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<ProcessResult> _run(
|
||||
String executable,
|
||||
List<String> args, {
|
||||
Directory? workingDirectory,
|
||||
}) async {
|
||||
final result = await Process.run(
|
||||
executable,
|
||||
args,
|
||||
workingDirectory: workingDirectory?.path,
|
||||
);
|
||||
if (result.exitCode != 0) {
|
||||
throw ProcessException(
|
||||
executable,
|
||||
args,
|
||||
[
|
||||
'exit code ${result.exitCode}',
|
||||
'stdout:',
|
||||
result.stdout,
|
||||
'stderr:',
|
||||
result.stderr,
|
||||
].join('\n'),
|
||||
result.exitCode,
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void _expectStatus(
|
||||
String output, {
|
||||
required String license,
|
||||
required bool proFeature,
|
||||
}) {
|
||||
final lines = const LineSplitter()
|
||||
.convert(output.trim())
|
||||
.where((line) => line.isNotEmpty)
|
||||
.toList();
|
||||
final expectedFeature = proFeature ? 'enabled' : 'off';
|
||||
if (!lines.contains('license:$license') ||
|
||||
!lines.contains('pro-feature:$expectedFeature')) {
|
||||
throw StateError(
|
||||
'Unexpected app status. Expected license:$license and '
|
||||
'pro-feature:$expectedFeature, got:\n$output',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _sha256File(File file) {
|
||||
final result = Process.runSync('certutil', [
|
||||
'-hashfile',
|
||||
file.path,
|
||||
'SHA256',
|
||||
]);
|
||||
if (result.exitCode != 0) {
|
||||
throw ProcessException(
|
||||
'certutil',
|
||||
['-hashfile', file.path, 'SHA256'],
|
||||
result.stderr.toString(),
|
||||
result.exitCode,
|
||||
);
|
||||
}
|
||||
final match = RegExp(
|
||||
r'\b[0-9a-fA-F]{64}\b',
|
||||
).firstMatch('${result.stdout}\n${result.stderr}');
|
||||
if (match == null) {
|
||||
throw StateError('Could not parse SHA256 hash for ${file.path}.');
|
||||
}
|
||||
return match.group(0)!.toLowerCase();
|
||||
}
|
||||
Reference in New Issue
Block a user