181 lines
5.1 KiB
Dart
181 lines
5.1 KiB
Dart
import 'dart:io';
|
|
|
|
import 'package:uuid/uuid.dart';
|
|
|
|
import '../../../about/internal/application/DeploymentRecord.dart';
|
|
import '../../../about/internal/application/ServerProfile.dart';
|
|
import '../../../about/internal/application/ServiceConfig.dart';
|
|
import '../../../about/internal/application/StepTracker.dart';
|
|
import '../../../comms/Comms.dart' show DeployResult, LogLine;
|
|
import '../../../utils/CommonUtils.dart';
|
|
import '../../parent/ParentViewModel.dart';
|
|
import 'ConnectBatchDeploy.dart';
|
|
|
|
class ViewBatchDeploy extends ParentViewModel {
|
|
ConnectBatchDeploy connection;
|
|
|
|
ViewBatchDeploy(super.context, this.connection);
|
|
|
|
bool _cancelRequested = false;
|
|
|
|
Stream<LogLine> get logs => getDataManager().logs;
|
|
|
|
Stream<LogLine> get buildLogs => getDataManager().buildLogs;
|
|
|
|
Stream<List<DeployStep>> get steps => getDataManager().stepStream;
|
|
|
|
Stream<List<DeployStep>> get buildSteps => getDataManager().buildStepStream;
|
|
|
|
/// Builds (and optionally deploys) each service in [batch], in order. One
|
|
/// service failing doesn't stop the rest — the run reports per-service.
|
|
void runBatch(
|
|
ServerProfile server,
|
|
List<ServiceConfig> batch, {
|
|
required bool deployAfterBuild,
|
|
}) async {
|
|
if (deployAfterBuild &&
|
|
!await canReach(
|
|
server,
|
|
() => runBatch(server, batch, deployAfterBuild: deployAfterBuild),
|
|
)) {
|
|
return;
|
|
}
|
|
|
|
_cancelRequested = false;
|
|
connection.onBatchStarted(batch.map((s) => s.id).toList());
|
|
|
|
for (final service in batch) {
|
|
if (_cancelRequested) {
|
|
connection.onServiceState(service.id, BatchState.skipped);
|
|
continue;
|
|
}
|
|
await _runOne(server, service, deployAfterBuild);
|
|
}
|
|
|
|
connection.onBatchFinished();
|
|
}
|
|
|
|
Future<void> _runOne(
|
|
ServerProfile server,
|
|
ServiceConfig service,
|
|
bool deployAfterBuild,
|
|
) async {
|
|
connection.onServiceState(service.id, BatchState.building);
|
|
|
|
final built = service.isWeb
|
|
? await getDataManager().buildWeb(service)
|
|
: await getDataManager().build(service);
|
|
|
|
if (!built.ok) {
|
|
connection.onServiceState(
|
|
service.id,
|
|
BatchState.failed,
|
|
detail: built.message,
|
|
);
|
|
return;
|
|
}
|
|
|
|
if (!deployAfterBuild) {
|
|
connection.onServiceState(service.id, BatchState.done, detail: 'Built');
|
|
return;
|
|
}
|
|
|
|
connection.onServiceState(service.id, BatchState.deploying);
|
|
|
|
DeployResult result;
|
|
String artifact;
|
|
try {
|
|
if (service.isWeb) {
|
|
final directory = built.dir!.path;
|
|
if (!File('$directory/index.html').existsSync()) {
|
|
connection.onServiceState(
|
|
service.id,
|
|
BatchState.failed,
|
|
detail: 'No index.html in build output.',
|
|
);
|
|
return;
|
|
}
|
|
final (bytes, count) = await getDataManager().packageWeb(directory);
|
|
artifact = service.framework.label;
|
|
result = await getDataManager().deployWeb(
|
|
server: server,
|
|
service: service,
|
|
archiveBytes: bytes,
|
|
fileCount: count,
|
|
);
|
|
} else {
|
|
final jar = built.jar!;
|
|
artifact = baseName(jar.path);
|
|
result = await getDataManager().deploy(
|
|
server: server,
|
|
service: service,
|
|
jarBytes: await jar.readAsBytes(),
|
|
jarLabel: artifact,
|
|
);
|
|
}
|
|
} catch (error) {
|
|
connection.onServiceState(
|
|
service.id,
|
|
BatchState.failed,
|
|
detail: 'Error: $error',
|
|
);
|
|
return;
|
|
}
|
|
|
|
await _record(server, service, result, artifact);
|
|
connection.onServiceState(
|
|
service.id,
|
|
result.ok ? BatchState.done : BatchState.failed,
|
|
detail: result.message,
|
|
);
|
|
}
|
|
|
|
/// Stops after the service currently running finishes.
|
|
void requestCancel() {
|
|
_cancelRequested = true;
|
|
getDataManager().requestCancel();
|
|
getDataManager().cancelBuild();
|
|
}
|
|
|
|
/// Remembers the order services should be deployed in next time.
|
|
void saveDeployOrder(ServerProfile server, List<String> ids) async {
|
|
try {
|
|
final servers = await getDataManager().getServers();
|
|
final index = servers.indexWhere((s) => s.id == server.id);
|
|
if (index < 0) return;
|
|
servers[index].deployOrder = ids;
|
|
await getDataManager().setServers(servers);
|
|
} catch (error) {
|
|
handleError(error, () => saveDeployOrder(server, ids), () {}, "Retry");
|
|
}
|
|
}
|
|
|
|
Future<void> _record(
|
|
ServerProfile server,
|
|
ServiceConfig service,
|
|
DeployResult result,
|
|
String artifact,
|
|
) async {
|
|
try {
|
|
await getDataManager().addDeployment(
|
|
DeploymentRecord(
|
|
id: const Uuid().v4(),
|
|
serverId: server.id,
|
|
serverName: server.name,
|
|
serviceId: service.id,
|
|
serviceLabel: service.name,
|
|
jarName: artifact,
|
|
remotePath: service.isWeb
|
|
? service.remoteRoot
|
|
: service.remotePath(artifact),
|
|
time: DateTime.now(),
|
|
status: result.ok ? DeployStatus.success : DeployStatus.failure,
|
|
message: result.message,
|
|
),
|
|
);
|
|
} catch (_) {
|
|
// A missing history row must not change what the batch reports.
|
|
}
|
|
}
|
|
}
|