appsok/lib/src/models/install_attempt_result.dart

99 lines
3 KiB
Dart

import '../services/adb_service.dart';
import 'adb_device.dart';
import 'pending_install.dart';
enum InstallAttemptOutcome { success, failure }
class InstallAttemptResult {
const InstallAttemptResult({
required this.pendingInstall,
required this.device,
required this.result,
});
final PendingInstall pendingInstall;
final AdbDevice device;
final AdbInstallResult result;
InstallAttemptOutcome get outcome => result.succeeded
? InstallAttemptOutcome.success
: InstallAttemptOutcome.failure;
bool get succeeded => outcome == InstallAttemptOutcome.success;
bool get failed => outcome == InstallAttemptOutcome.failure;
String get artifactLabel =>
'#${pendingInstall.buildNumber} · ${pendingInstall.jobName} · '
'${pendingInstall.fileName}';
String get outputSummary {
final stdout = result.stdout.trim();
final stderr = result.stderr.trim();
if (succeeded) {
return [stdout, stderr].where((it) => it.isNotEmpty).join('\n');
}
if (stderr.isNotEmpty) return _firstNonEmptyLine(stderr);
if (stdout.isNotEmpty) return _firstNonEmptyLine(stdout);
return '설치 실패';
}
String get maskedOutputSummary => _maskSensitive(outputSummary);
String get maskedDeviceSerial => _maskSerial(device.serial);
String get maskedReport {
final lines = [
'AppSok 설치 리포트',
'결과: ${succeeded ? '성공' : '실패'}',
'exitCode: ${result.exitCode}',
'Build: $artifactLabel',
'Device: ${device.displayName} ($maskedDeviceSerial)',
if (maskedOutputSummary.isNotEmpty) '요약: $maskedOutputSummary',
if (result.stdout.trim().isNotEmpty)
'stdout:\n${_maskSensitive(result.stdout.trim())}',
if (result.stderr.trim().isNotEmpty)
'stderr:\n${_maskSensitive(result.stderr.trim())}',
];
return lines.join('\n');
}
String _firstNonEmptyLine(String text) {
final lines = text
.split('\n')
.map((line) => line.trim())
.where((line) => line.isNotEmpty);
return lines.isEmpty ? '' : lines.first;
}
String _maskSensitive(String value) {
final masked = value
.replaceAll(RegExp(r'https?:\/\/[^\s]+'), '[masked-url]')
.replaceAllMapped(
RegExp(
r'\b(authorization)\s*[:=]\s*bearer\s+[A-Za-z0-9._~+/=-]+',
caseSensitive: false,
),
(match) => '${match.group(1)}: [masked]',
)
.replaceAllMapped(
RegExp(
r'\b(api[_-]?token|token|password|passwd|secret)\s*[:=]\s*[^\s&]+',
caseSensitive: false,
),
(match) => '${match.group(1)}=[masked]',
);
return device.serial.isEmpty
? masked
: masked.replaceAll(device.serial, maskedDeviceSerial);
}
String _maskSerial(String serial) {
if (serial.isEmpty) return '[masked-serial]';
if (serial.length <= 4) return '*' * serial.length;
final prefix = serial.substring(0, 2);
final suffix = serial.substring(serial.length - 2);
return '$prefix${'*' * (serial.length - 4)}$suffix';
}
}