diff --git a/packages/unified_distributor/lib/src/cli/command_package.dart b/packages/unified_distributor/lib/src/cli/command_package.dart index 630c8e06..d710321f 100644 --- a/packages/unified_distributor/lib/src/cli/command_package.dart +++ b/packages/unified_distributor/lib/src/cli/command_package.dart @@ -92,6 +92,12 @@ class CommandPackage extends Command { 'You may add multiple \'--build-dart-define key=value\' pairs', ].join('\n'), ); + + argParser.addOption( + 'post-package-windows-cmd', + valueHelp: 'command', + help: 'Command to execute after Windows build but before packaging (e.g., "call ./sign-file.bat")', + ); } final UnifiedDistributor distributor; @@ -118,6 +124,7 @@ class CommandPackage extends Command { final String? artifactName = argResults?['artifact-name']; final String? flutterBuildArgs = argResults?['flutter-build-args']; final bool isSkipClean = argResults?.wasParsed('skip-clean') ?? false; + final String? postPackageWindowsCmd = argResults?['post-package-windows-cmd']; final Map buildArguments = _generateBuildArgs(flutterBuildArgs); @@ -139,6 +146,7 @@ class CommandPackage extends Command { artifactName: artifactName, cleanBeforeBuild: !isSkipClean, buildArguments: buildArguments, + postPackageWindowsCmd: postPackageWindowsCmd, ); } diff --git a/packages/unified_distributor/lib/src/unified_distributor.dart b/packages/unified_distributor/lib/src/unified_distributor.dart index 80feb970..b9d85194 100644 --- a/packages/unified_distributor/lib/src/unified_distributor.dart +++ b/packages/unified_distributor/lib/src/unified_distributor.dart @@ -144,6 +144,7 @@ class UnifiedDistributor { required bool cleanBeforeBuild, required Map buildArguments, Map? variables, + String? postPackageWindowsCmd, }) async { List makeResultList = []; @@ -161,7 +162,7 @@ class UnifiedDistributor { BuildResult? buildResult; for (String target in targets) { - logger.info('Packaging ${pubspec.name} ${pubspec.version} as $target:'); + logger.info('Packaging ${pubspec.name} ${pubspec.version} as $target for ${platform.toLowerCase()}:'); if (!isBuildOnlyOnce || (isBuildOnlyOnce && buildResult == null)) { try { buildResult = await _builder.build( @@ -186,6 +187,72 @@ class UnifiedDistributor { } if (buildResult != null) { + // Execute custom Windows command if specified and platform is Windows + if (platform.toLowerCase() == 'windows' && + postPackageWindowsCmd != null && + postPackageWindowsCmd.isNotEmpty) { + logger.info('Executing post-package Windows command: $postPackageWindowsCmd'); + try { + // Create a temporary batch file to execute the command + final tempDir = Directory.systemTemp; + final tempFile = File('${tempDir.path}\\fastforge_temp_cmd_${DateTime.now().millisecondsSinceEpoch}.bat'); + + try { + // Process the command to properly handle paths with spaces + String processedCmd = postPackageWindowsCmd; + + // Find all quoted paths and ensure they're properly formatted + final pathRegex = RegExp(r'"([^"]+)"'); + processedCmd = processedCmd.replaceAllMapped(pathRegex, (match) { + String path = match.group(1)!; + // Remove any existing escaping to prevent double escaping + path = path.replaceAll(r'\\', r'\'); + // Ensure the path is properly formatted for Windows + path = path.replaceAll(r'/', r'\'); + return '"$path"'; + }); + + // Write the command to the batch file + tempFile.writeAsStringSync('@echo off\nsetlocal enabledelayedexpansion\n$processedCmd', flush: true); + logger.info('Created temporary batch file: ${tempFile.path}'); + logger.info('Processed command: $processedCmd'); + + // Execute the temporary batch file + ProcessResult result = await Process.run( + tempFile.path, + [], + workingDirectory: Directory.current.path, + ); + + if (result.exitCode == 0) { + logger.info('Post-package command executed successfully'.brightGreen()); + if (result.stdout.toString().isNotEmpty) { + print(result.stdout); + } + } else { + logger.severe('Post-package command failed with exit code ${result.exitCode}'.red()); + if (result.stderr.toString().isNotEmpty) { + logger.severe(result.stderr.toString().red()); + } + if (result.stdout.toString().isNotEmpty) { + logger.severe(result.stdout.toString().red()); + } + throw Exception('Post-build command failed'); + } + } finally { + // Clean up the temporary file + if (tempFile.existsSync()) { + tempFile.deleteSync(); + logger.info('Cleaned up temporary batch file'); + } + } + } catch (error) { + logger.severe('Failed to execute post-build command: $error'.red()); + rethrow; + } + } + + String buildMode = buildArguments.containsKey('profile') ? 'profile' : 'release'; Map? arguments = {