-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(build_transformer): move api release download and extraction…
… to separate files (#23) * refactor(build_transformer): move artefact download ops * fix(build_transformer): reduce log verbosity limit stacktraces to SEVERE and above
- Loading branch information
1 parent
afcdfd9
commit 0d2f45a
Showing
7 changed files
with
523 additions
and
302 deletions.
There are no files selected for viewing
41 changes: 41 additions & 0 deletions
41
...omodo_wallet_build_transformer/lib/src/steps/defi_api_build_step/artefact_downloader.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import 'dart:io'; | ||
|
||
import 'package:http/http.dart' as http; | ||
import 'package:komodo_wallet_build_transformer/src/steps/models/api/api_file_matching_config.dart'; | ||
|
||
abstract class ArtefactDownloader { | ||
ArtefactDownloader({ | ||
required this.apiCommitHash, | ||
required this.sourceUrl, | ||
required this.apiBranch, | ||
}); | ||
|
||
final String apiCommitHash; | ||
final String sourceUrl; | ||
final String apiBranch; | ||
|
||
Future<String> fetchDownloadUrl( | ||
ApiFileMatchingConfig matchingConfig, | ||
String platform, | ||
); | ||
|
||
Future<String> downloadArtefact({ | ||
required String url, | ||
required String destinationPath, | ||
}); | ||
|
||
Future<void> extractArtefact({ | ||
required String filePath, | ||
required String destinationFolder, | ||
}); | ||
} | ||
|
||
extension ResponseCode on http.Response { | ||
void throwIfNotSuccessResponse() { | ||
if (statusCode != 200) { | ||
throw HttpException( | ||
'Failed to fetch data: $statusCode $reasonPhrase', | ||
); | ||
} | ||
} | ||
} |
49 changes: 49 additions & 0 deletions
49
...llet_build_transformer/lib/src/steps/defi_api_build_step/artefact_downloader_factory.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import 'package:komodo_wallet_build_transformer/src/steps/defi_api_build_step/artefact_downloader.dart'; | ||
import 'package:komodo_wallet_build_transformer/src/steps/defi_api_build_step/dev_builds_artefact_downloader.dart'; | ||
import 'package:komodo_wallet_build_transformer/src/steps/defi_api_build_step/github_artefact_downloader.dart'; | ||
import 'package:komodo_wallet_build_transformer/src/steps/github/github_api_provider.dart'; | ||
import 'package:komodo_wallet_build_transformer/src/steps/models/api/api_build_config.dart'; | ||
|
||
class ArtefactDownloaderFactory { | ||
static Map<String, ArtefactDownloader> fromBuildConfig( | ||
ApiBuildConfig buildConfig, { | ||
String? githubToken, | ||
}) { | ||
final sourceUrls = buildConfig.sourceUrls; | ||
final downloaders = <String, ArtefactDownloader>{}; | ||
for (final sourceUrl in sourceUrls) { | ||
if (sourceUrl.startsWith('https://api.github.com/repos/')) { | ||
downloaders[sourceUrl] = createGithubArtefactDownloader( | ||
buildConfig, | ||
sourceUrl, | ||
githubToken: githubToken, | ||
); | ||
} else { | ||
downloaders[sourceUrl] = DevBuildsArtefactDownloader( | ||
apiBranch: buildConfig.branch, | ||
apiCommitHash: buildConfig.apiCommitHash, | ||
sourceUrl: sourceUrl, | ||
); | ||
} | ||
} | ||
return downloaders; | ||
} | ||
|
||
static ArtefactDownloader createGithubArtefactDownloader( | ||
ApiBuildConfig buildConfig, | ||
String sourceUrl, { | ||
String? githubToken, | ||
}) { | ||
final apiProvider = GithubApiProvider.withBaseUrl( | ||
baseUrl: buildConfig.sourceUrls.first, | ||
branch: buildConfig.branch, | ||
token: githubToken, | ||
); | ||
return GithubArtefactDownloader( | ||
apiCommitHash: buildConfig.apiCommitHash, | ||
apiBranch: buildConfig.branch, | ||
sourceUrl: buildConfig.sourceUrls.first, | ||
githubApiProvider: apiProvider, | ||
); | ||
} | ||
} |
136 changes: 136 additions & 0 deletions
136
...t_build_transformer/lib/src/steps/defi_api_build_step/dev_builds_artefact_downloader.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
import 'dart:io'; | ||
|
||
import 'package:html/parser.dart' as parser; | ||
import 'package:http/http.dart' as http; | ||
import 'package:komodo_wallet_build_transformer/src/steps/defi_api_build_step/artefact_downloader.dart'; | ||
import 'package:komodo_wallet_build_transformer/src/steps/models/api/api_file_matching_config.dart'; | ||
import 'package:logging/logging.dart'; | ||
import 'package:path/path.dart' as path; | ||
|
||
class DevBuildsArtefactDownloader implements ArtefactDownloader { | ||
DevBuildsArtefactDownloader({ | ||
required this.apiBranch, | ||
required this.apiCommitHash, | ||
required this.sourceUrl, | ||
}); | ||
|
||
final _log = Logger('DevBuildsArtefactDownloader'); | ||
|
||
@override | ||
final String apiBranch; | ||
|
||
@override | ||
final String apiCommitHash; | ||
|
||
@override | ||
final String sourceUrl; | ||
|
||
@override | ||
Future<String> fetchDownloadUrl( | ||
ApiFileMatchingConfig matchingConfig, | ||
String platform, | ||
) async { | ||
final url = '$sourceUrl/$apiBranch/'; | ||
final response = await http.get(Uri.parse(url)); | ||
response.throwIfNotSuccessResponse(); | ||
|
||
final document = parser.parse(response.body); | ||
final extensions = ['.zip']; | ||
|
||
// Support both full and short hash variants | ||
final fullHash = apiCommitHash; | ||
final shortHash = apiCommitHash.substring(0, 7); | ||
_log.info('Looking for files with hash $fullHash or $shortHash'); | ||
|
||
// Look for files with either hash length | ||
final attemptedFiles = <String>[]; | ||
for (final element in document.querySelectorAll('a')) { | ||
final href = element.attributes['href']; | ||
if (href != null) attemptedFiles.add(href); | ||
if (href != null && | ||
matchingConfig.matches(href) && | ||
extensions.any(href.endsWith)) { | ||
if (href.contains(fullHash) || href.contains(shortHash)) { | ||
_log.info('Found matching file: $href'); | ||
return '$sourceUrl/$apiBranch/$href'; | ||
} | ||
} | ||
} | ||
|
||
final availableAssets = attemptedFiles.join('\n . - '); | ||
_log.fine('No matching files found in $sourceUrl. ' | ||
'\nPattern: ${matchingConfig.matchingPattern}, ' | ||
'\nHashes tried: [$fullHash, $shortHash]' | ||
'\nAvailable assets: $availableAssets'); | ||
|
||
throw Exception('Zip file not found for platform $platform'); | ||
} | ||
|
||
@override | ||
Future<String> downloadArtefact({ | ||
required String url, | ||
required String destinationPath, | ||
}) async { | ||
_log.info('Downloading $url...'); | ||
final response = await http.get(Uri.parse(url)); | ||
response.throwIfNotSuccessResponse(); | ||
|
||
final zipFileName = path.basename(url); | ||
final zipFilePath = path.join(destinationPath, zipFileName); | ||
|
||
final directory = Directory(destinationPath); | ||
if (!directory.existsSync()) { | ||
await directory.create(recursive: true); | ||
} | ||
|
||
final zipFile = File(zipFilePath); | ||
try { | ||
await zipFile.writeAsBytes(response.bodyBytes); | ||
} catch (e) { | ||
_log.info('Error writing file', e); | ||
rethrow; | ||
} | ||
|
||
_log.info('Downloaded $zipFileName'); | ||
return zipFilePath; | ||
} | ||
|
||
@override | ||
Future<void> extractArtefact({ | ||
required String filePath, | ||
required String destinationFolder, | ||
}) async { | ||
try { | ||
// Determine the platform to use the appropriate extraction command | ||
if (Platform.isMacOS || Platform.isLinux) { | ||
// For macOS and Linux, use the `unzip` command with overwrite option | ||
final result = await Process.run( | ||
'unzip', | ||
['-o', filePath, '-d', destinationFolder], | ||
); | ||
if (result.exitCode != 0) { | ||
throw Exception('Error extracting zip file: ${result.stderr}'); | ||
} | ||
} else if (Platform.isWindows) { | ||
// For Windows, use PowerShell's Expand-Archive command | ||
final result = await Process.run('powershell', [ | ||
'Expand-Archive', | ||
'-Path', | ||
filePath, | ||
'-DestinationPath', | ||
destinationFolder, | ||
]); | ||
if (result.exitCode != 0) { | ||
throw Exception('Error extracting zip file: ${result.stderr}'); | ||
} | ||
} else { | ||
_log.severe('Unsupported platform: ${Platform.operatingSystem}'); | ||
throw UnsupportedError('Unsupported platform'); | ||
} | ||
_log.info('Extraction completed.'); | ||
} catch (e) { | ||
_log.shout('Failed to extract zip file: $e'); | ||
rethrow; | ||
} | ||
} | ||
} |
166 changes: 166 additions & 0 deletions
166
...allet_build_transformer/lib/src/steps/defi_api_build_step/github_artefact_downloader.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
import 'dart:io'; | ||
|
||
import 'package:http/http.dart' as http; | ||
import 'package:komodo_wallet_build_transformer/src/steps/defi_api_build_step/artefact_downloader.dart'; | ||
import 'package:komodo_wallet_build_transformer/src/steps/github/github_api_provider.dart'; | ||
import 'package:komodo_wallet_build_transformer/src/steps/models/api/api_file_matching_config.dart'; | ||
import 'package:logging/logging.dart'; | ||
import 'package:path/path.dart' as path; | ||
|
||
class GithubArtefactDownloader implements ArtefactDownloader { | ||
GithubArtefactDownloader({ | ||
required this.apiBranch, | ||
required this.apiCommitHash, | ||
required this.sourceUrl, | ||
required this.githubApiProvider, | ||
}); | ||
|
||
final _log = Logger('GithubArtefactDownloader'); | ||
|
||
final GithubApiProvider githubApiProvider; | ||
|
||
@override | ||
final String apiBranch; | ||
|
||
@override | ||
final String apiCommitHash; | ||
|
||
@override | ||
final String sourceUrl; | ||
|
||
@override | ||
Future<String> fetchDownloadUrl( | ||
ApiFileMatchingConfig matchingConfig, | ||
String platform, | ||
) async { | ||
final releases = await githubApiProvider.getReleases(); | ||
final fullHash = apiCommitHash; | ||
final shortHash = apiCommitHash.substring(0, 7); | ||
|
||
_log.info('Looking for release files with hash $fullHash or $shortHash'); | ||
|
||
// TODO! Try to find exact version release first | ||
// if (version != null && version!.isNotEmpty) { | ||
// _log.info('Searching for exact version match: $version'); | ||
// for (final release in releases) { | ||
// if (release.tagName == version) { | ||
// _log.info('Found matching release: ${release.tagName}'); | ||
// for (final asset in release.assets) { | ||
// final fileName = path.basename(asset.browserDownloadUrl); | ||
// _log.fine('Checking file $fileName for $platform'); | ||
|
||
// if (matchingConfig.matches(fileName)) { | ||
// _log.info('Found matching file $fileName in version $version'); | ||
// return asset.browserDownloadUrl; | ||
// } | ||
// } | ||
// _log.warning('No matching assets found in version $version. ' | ||
// 'Available assets:\n${release.assets.map((a) => ' - ${a.name}').join('\n')}'); | ||
// } | ||
// } | ||
// _log.warning('No exact version match found for $version'); | ||
// } | ||
|
||
// If no exact version match found, try matching by commit hash | ||
_log.info('Searching for commit hash match'); | ||
for (final release in releases) { | ||
for (final asset in release.assets) { | ||
final fileName = path.basename(asset.browserDownloadUrl); | ||
|
||
if (matchingConfig.matches(fileName)) { | ||
if (fileName.contains(fullHash) || fileName.contains(shortHash)) { | ||
final commitHash = await githubApiProvider.getLatestCommitHash( | ||
branch: release.tagName, | ||
); | ||
if (commitHash == apiCommitHash) { | ||
_log.info('Found matching file by commit hash: $fileName'); | ||
return asset.browserDownloadUrl; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
// Log available assets to help diagnose issues | ||
final releaseAssets = | ||
releases.expand((r) => r.assets).map((a) => ' - ${a.name}').join('\n'); | ||
_log.fine('No files found matching criteria:\n' | ||
'Platform: $platform\n' | ||
'Version: \$version\n' | ||
'Hash: $fullHash or $shortHash\n' | ||
'Pattern: ${matchingConfig.matchingPattern}\n' | ||
'Available assets:\n$releaseAssets'); | ||
|
||
throw Exception( | ||
'Zip file not found for platform $platform in GitHub releases. ' | ||
'Searched for version: \$version, commit: $apiCommitHash'); | ||
} | ||
|
||
@override | ||
Future<String> downloadArtefact({ | ||
required String url, | ||
required String destinationPath, | ||
}) async { | ||
_log.info('Downloading $url...'); | ||
final response = await http.get(Uri.parse(url)); | ||
response.throwIfNotSuccessResponse(); | ||
|
||
final zipFileName = path.basename(url); | ||
final zipFilePath = path.join(destinationPath, zipFileName); | ||
|
||
final directory = Directory(destinationPath); | ||
if (!directory.existsSync()) { | ||
await directory.create(recursive: true); | ||
} | ||
|
||
final zipFile = File(zipFilePath); | ||
try { | ||
await zipFile.writeAsBytes(response.bodyBytes); | ||
} catch (e) { | ||
_log.info('Error writing file', e); | ||
rethrow; | ||
} | ||
|
||
_log.info('Downloaded $zipFileName'); | ||
return zipFilePath; | ||
} | ||
|
||
@override | ||
Future<void> extractArtefact({ | ||
required String filePath, | ||
required String destinationFolder, | ||
}) async { | ||
try { | ||
// Determine the platform to use the appropriate extraction command | ||
if (Platform.isMacOS || Platform.isLinux) { | ||
// For macOS and Linux, use the `unzip` command with overwrite option | ||
final result = await Process.run( | ||
'unzip', | ||
['-o', filePath, '-d', destinationFolder], | ||
); | ||
if (result.exitCode != 0) { | ||
throw Exception('Error extracting zip file: ${result.stderr}'); | ||
} | ||
} else if (Platform.isWindows) { | ||
// For Windows, use PowerShell's Expand-Archive command | ||
final result = await Process.run('powershell', [ | ||
'Expand-Archive', | ||
'-Path', | ||
filePath, | ||
'-DestinationPath', | ||
destinationFolder, | ||
]); | ||
if (result.exitCode != 0) { | ||
throw Exception('Error extracting zip file: ${result.stderr}'); | ||
} | ||
} else { | ||
_log.severe('Unsupported platform: ${Platform.operatingSystem}'); | ||
throw UnsupportedError('Unsupported platform'); | ||
} | ||
_log.info('Extraction completed.'); | ||
} catch (e) { | ||
_log.shout('Failed to extract zip file: $e'); | ||
rethrow; | ||
} | ||
} | ||
} |
Oops, something went wrong.