From a234dcfb1395579fa3845e7a091414e9a647e48a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 25 Aug 2025 15:43:03 +0000 Subject: [PATCH 1/2] Initial plan From 0858f0283c225b5e305a7ec49254425a276d4e61 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 25 Aug 2025 15:52:59 +0000 Subject: [PATCH 2/2] Add --target-path support for local release builds Co-authored-by: greymag <1502131+greymag@users.noreply.github.com> --- .../release/start_release_command.dart | 64 ++++++++++++++++++- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/lib/commands/release/start_release_command.dart b/lib/commands/release/start_release_command.dart index 5b3401d..08347a4 100644 --- a/lib/commands/release/start_release_command.dart +++ b/lib/commands/release/start_release_command.dart @@ -30,6 +30,7 @@ class StartReleaseCommand extends AlexCommand with IntlMixin { static const _argLocal = 'local'; static const _argEntryPoint = 'entry-point'; static const _argBuildPlatforms = 'platforms'; + static const _argTargetPath = 'target-path'; static const _argSkipL10n = 'skip_l10n'; static const _argDemo = 'demo'; @@ -74,6 +75,13 @@ class StartReleaseCommand extends AlexCommand with IntlMixin { defaultsTo: [_BuildPlatform.android, _BuildPlatform.ios].asArgs(), valueHelp: 'PLATFORMS', ) + ..addOption( + _argTargetPath, + abbr: 't', + help: 'Target directory path where build artifacts will be copied. ' + 'Only for local release builds.', + valueHelp: 'DIR_PATH', + ) ..addFlag( _argDemo, help: "Runs command in demonstration mode", @@ -195,13 +203,14 @@ class StartReleaseCommand extends AlexCommand with IntlMixin { if (isLocalRelease) { final entryPoint = args[_argEntryPoint] as String?; + final targetPath = args[_argTargetPath] as String?; final platforms = _BuildPlatform.parseArgs(args[_argBuildPlatforms] as String); printVerbose('Platforms: ${platforms.asDesc()}'); for (final platform in platforms) { - final localBuildResult = await _localBuild(entryPoint, platform); + final localBuildResult = await _localBuild(entryPoint, platform, targetPath); if (localBuildResult != 0) return localBuildResult; } } @@ -539,7 +548,7 @@ class StartReleaseCommand extends AlexCommand with IntlMixin { return 0; } - Future _localBuild(String? entryPoint, _BuildPlatform platform) async { + Future _localBuild(String? entryPoint, _BuildPlatform platform, String? targetPath) async { printInfo('Run local build for ${platform.name}'); final String subcommand; @@ -577,7 +586,15 @@ class StartReleaseCommand extends AlexCommand with IntlMixin { final buildLine = message ?.split('\n') .firstWhere((line) => line.contains('✓ Built '), orElse: () => ''); - if (buildLine != null && buildLine.isNotEmpty) printInfo(buildLine); + + if (buildLine != null && buildLine.isNotEmpty) { + printInfo(buildLine); + + // Copy build artifact to target path if specified + if (targetPath != null) { + await _copyBuildArtifact(buildLine, targetPath, platform); + } + } return 0; } else { return error( @@ -588,6 +605,47 @@ class StartReleaseCommand extends AlexCommand with IntlMixin { ); } } + + Future _copyBuildArtifact(String buildLine, String targetPath, _BuildPlatform platform) async { + // Extract the source file path from the build output line + // Examples: + // ✓ Built build/app/outputs/bundle/release/app-release.aab (27.1MB). + // ✓ Built build/ios/ipa/myapp.ipa (45.2MB). + + final builtPattern = RegExp(r'✓ Built (.+?) \('); + final match = builtPattern.firstMatch(buildLine); + + if (match == null) { + printWarning('Could not parse build output to find artifact path: $buildLine'); + return; + } + + final sourcePath = match.group(1)!; + final sourceFile = File(sourcePath); + + if (!await sourceFile.exists()) { + printWarning('Build artifact not found at: $sourcePath'); + return; + } + + // Ensure target directory exists + final targetDir = Directory(targetPath); + if (!await targetDir.exists()) { + await targetDir.create(recursive: true); + printInfo('Created target directory: $targetPath'); + } + + // Copy the file to target directory + final fileName = p.basename(sourcePath); + final targetFile = File(p.join(targetPath, fileName)); + + try { + await sourceFile.copy(targetFile.path); + printInfo('Copied ${platform.name} build artifact to: ${targetFile.path}'); + } catch (e) { + printWarning('Failed to copy build artifact to target path: $e'); + } + } } class Entry {