import 'dart:convert'; import 'dart:io'; import 'package:crypto/crypto.dart'; Future main(List args) async { final version = args.isEmpty ? '1.0.0' : args[0]; final code = args.length < 2 ? 1 : int.parse(args[1]); final fileName = 'ikar-$version+$code.apk'; final reportSuffix = code == 1 ? '' : '-$version'; final base = Uri.parse('https://argus.kusoft.xyz'); final http = HttpClient()..connectionTimeout = const Duration(seconds: 15); final local = File('artifacts/$fileName'); final downloaded = File('artifacts/ikar-$version+$code.argus.apk'); try { Future getJson(String path) async { final response = await (await http.getUrl( base.resolve(path), )).close().timeout(const Duration(seconds: 20)); if (response.statusCode != 200) { throw StateError('HTTP ${response.statusCode} for $path'); } return jsonDecode( await utf8.decodeStream(response).timeout(const Duration(seconds: 20)), ); } final results = await Future.wait([ getJson('/api/apps/ikar/manifest?platform=android&channel=stable'), getJson('/api/apps'), getJson('/health'), ]); final manifest = results[0] as Map; final release = manifest['release'] as Map; if (manifest['slug'] != 'ikar' || manifest['name'] != 'ИКАР' || release['version'] != version || release['androidVersionCode'] != code || release['platform'] != 'android' || release['channel'] != 'stable' || release['packageKind'] != 'apk' || release['originalFileName'] != fileName) { throw StateError('Incorrect public application or release identity'); } final catalog = results[1] as List; final listed = catalog.singleWhere((app) => app['slug'] == 'ikar'); if (listed['name'] != 'ИКАР' || listed['latestRelease']['id'] != release['id'] || (results[2] as Map)['status'] != 'ok') { throw StateError('Catalog or service health verification failed'); } final uri = base.resolve(release['downloadPath'] as String); if (uri.scheme != 'https' || uri.host != base.host || !uri.path.startsWith('/api/apps/ikar/releases/')) { throw StateError('Unexpected public download address'); } final response = await (await http.getUrl( uri, )).close().timeout(const Duration(seconds: 20)); if (response.statusCode != 200 || response.headers.contentType?.mimeType != 'application/vnd.android.package-archive') { throw StateError('APK download unavailable'); } await response .timeout(const Duration(seconds: 30)) .pipe(downloaded.openWrite()); final localDigest = (await sha256.bind(local.openRead()).first).toString(); final publicDigest = (await sha256.bind(downloaded.openRead()).first) .toString(); final size = await downloaded.length(); if (publicDigest != localDigest || publicDigest != release['sha256'] || size != await local.length() || size != release['packageSizeBytes']) { throw StateError('Public APK differs from the verified local artifact'); } final report = { 'passed': true, 'slug': 'ikar', 'name': 'ИКАР', 'package': 'ru.ikar.app', 'version': version, 'androidVersionCode': code, 'releaseId': release['id'], 'sha256': publicDigest, 'bytes': size, 'tlsValidation': true, 'listedInCatalog': true, 'serviceHealth': 'ok', 'cardUrl': 'https://argus.kusoft.xyz/#app=ikar', 'downloadUrl': uri.toString(), 'timestamp': DateTime.now().toUtc().toIso8601String(), }; const encoder = JsonEncoder.withIndent(' '); await File( 'artifacts/argus-manifest$reportSuffix.json', ).writeAsString(encoder.convert(manifest)); await File( 'artifacts/argus-public-verification$reportSuffix.json', ).writeAsString(encoder.convert(report)); stdout.writeln(jsonEncode(report)); } finally { http.close(force: true); } }