handle method
Execute the command. Returns the process exit code.
Implementation
@override
Future<int> handle(ArtisanContext ctx) async {
final state = await StateFile.read();
if (state == null) {
ctx.output.writeln('No state file; nothing to stop.');
return 0;
}
// The legacy pointer describes whichever app started last, so without
// this a stop from a project with nothing up reaches across and kills a
// sibling's running app, reporting a clean success while doing it.
final String? ownership = sessionOwnershipError(
state: state,
workingDirectory: Directory.current.path,
explicitStatePath: StateFile.explicitPath(),
);
if (ownership != null) {
ctx.output.error(ownership);
return 1;
}
// 1. flutter run process.
final pid = state['pid'] as int?;
if (pid != null) {
try {
stopKillFunction(pid, ProcessSignal.sigterm);
ctx.output.success('Sent SIGTERM to pid=$pid.');
} catch (e) {
ctx.output.warning('SIGTERM failed: $e (continuing).');
}
}
// 2. FIFO stdin holder (the `sleep infinity > fifo` background process
// that keeps the pipe's write end open across reload/hot-restart calls).
final holderPid = state['stdinHolderPid'] as int?;
if (holderPid != null) {
try {
stopKillFunction(holderPid, ProcessSignal.sigterm);
} catch (_) {
// Holder may already be gone; safe to ignore.
}
}
// 3. Named pipe file. Safe to delete even when readers/writers are
// still attached — POSIX unlinks the inode, fds stay valid until
// closed naturally.
final pipePath = state['stdinPipe'] as String?;
if (pipePath != null) {
try {
final pipe = File(pipePath);
if (pipe.existsSync()) await pipe.delete();
} catch (_) {
// Best-effort cleanup; don't block stop on FIFO removal failure.
}
}
// 4. Chrome process + tmp profile dir (CDP mode only; absent in non-CDP
// runs). Mirrors the SIGTERM-grace-SIGKILL-rm pattern from
// fluttersdk_dusk/lib/src/utils/chrome_reaper.dart without importing
// that package (no cross-package dep on a downstream plugin).
final chromePid = state['chromePid'] as int?;
if (chromePid != null) {
await _reapChrome(ctx, chromePid, state['tmpProfileDir'] as String?);
}
await StateFile.delete();
ctx.output.success('state.json removed.');
return 0;
}