Skip to content

0.0.17

Choose a tag to compare

@github-actions github-actions released this 29 Sep 17:49
· 4 commits to master since this release
de24668

Fixed

  • stop, restart and a failed start --cdp-port stop the app's whole process group and wait until it is gone. stop sent SIGTERM to the flutter tool pid and returned at once, so restart (and any caller starting right after a stop) raced the old tool for its web port and CDP port; a --cdp-port start failed on the spot with "Port ... is already in use". A timed-out --cdp-port start sent the same bare SIGTERM and returned, which ended the tool and orphaned its frontend_server to pid 1, where it kept compiling and loading the machine. The wrapper was already spawned in Dart's detached mode, which calls setsid(), so the tool, its FIFO holder and every child it starts share one process group of their own; nothing signalled it. The new ProcessGroupReaper reads the group from the live pid (ps -o pgid=, never stored, since a dead group's id can be reused; through the FIFO holder when the tool is already gone), SIGTERMs the group and waits up to 5 seconds for every member, SIGKILLs the group when a member is left and waits again, then waits up to 5 seconds for the ports the app held (a browser session's web port, Chrome's CDP port); a port that stays bound is reported and never answered with SIGKILL. It never signals the caller's own group or a pid of 1 or below, does not count zombies as live, reads a failed ps listing as alive rather than gone, falls back to a SIGTERM to the pid alone with a warning when ps cannot run or prints an unreadable elapsed time (so stop still finishes on a host without it), and leaves alone a recorded pid whose process started after the session's startedAt (ps -o etime=): a stale session's number may have been handed to an unrelated process, whose group would otherwise be SIGKILLed. stop deletes the session only after that; an app that outlives SIGKILL keeps its session and stop exits 1, which also stops restart from starting on top of it. A port still bound by someone else is a warning. Chrome is reaped the same way on both paths, since its helpers share its group. StartCommand.defaultPortProbe is no longer @visibleForTesting, because stop waits on the same probe start refuses a busy port with. (lib/src/commands/helpers/process_group_reaper.dart, lib/src/commands/stop_command.dart, lib/src/commands/start_command.dart, lib/src/mcp/mcp_server.dart, test/commands/, doc/commands/, doc/mcp/, skills/fluttersdk-artisan/references/)

  • start --cdp-port waits for the web server as long as --timeout allows. The wait for the is being served at line was a literal 60 seconds, so under load a web compile that ran past it failed the start although the default --timeout of 90 allowed more. It now uses the resolved --timeout, like the VM Service scrape after it; the cdpWebServerReadyWaiter test seam receives the timeout as its second argument.

  • start --profile-static reaches flutter run on a device, --timeout bounds every scrape, and stop stops the Android app. The flag was recorded as profile: static and never forwarded, so a "profile" run on an emulator was a debug build and every number taken from it said so. It is now sent as --profile on any non-web target, once even when --flutter-arg=--profile is passed as well; on a browser target (chrome, edge, web-server) it stays a label, since a web profile build has no VM Service, and the recorded state value is unchanged. The plain (non-CDP) branch scraped the VM Service URI for a literal 90 seconds while --timeout was parsed and validated, so a cold Android build could not be given more time; both branches now use the resolved value. stop signalled only the flutter tool, which detaches from the device without stopping the app, so the next cold start found the old process resident; on an Android serial (anything that is not a browser, a desktop target or an iOS id, legacy 40-hex UDIDs included) it now also runs adb -s <serial> shell am force-stop <applicationId>, reading the id from android/app/build.gradle or .kts, and reports a warning rather than failing when the id or adb is missing. The artisan_start profile-static descriptor states what the flag does. restart carries the build mode as well: it read the port, device and flutter arguments from state.json but not profile, so restarting between two measurements came back as a debug build and recorded profile: debug; it now replays profile: static, and restart --no-profile-static drops it. (lib/src/commands/restart_command.dart, lib/src/commands/start_command.dart, lib/src/commands/stop_command.dart, lib/src/mcp/mcp_server.dart, test/commands/, doc/commands/start.md, doc/mcp/tool-reference.md, skills/fluttersdk-artisan/references/mcp-tools.md)

  • bin/fsa releases its build lock before exec. The lock was released by an EXIT trap, and exec replaces the shell, so the trap never ran: after a rebuild, the long-lived process the wrapper exec'd into (typically mcp:serve) held .artisan/.fsa.lock for its whole life, and every other invocation that needed a build looped on "waiting for another fsa invocation to finish..." forever. The lock is now removed and the trap cleared before exec. A wait on a live owner is bounded by FSA_LOCK_TIMEOUT (default 600 seconds) and then fails naming the owner's pid, and a rebuild compiles into a fresh directory and swaps it in by rename, so a binary another process is running is never overwritten; a killed build's scratch directory is removed by the next build. Existing consumers pick this up with make:fast-cli --force.