Developer-Dashboard

 view release on metacpan or  search on metacpan

doc/testing.md  view on Meta::CPAN

- skill install progress rendering that keeps the epic checklist visible while
  streaming a rolling ten-line detail window under the active manifest step,
  with `CLI::Progress` coverage guarding both the rolling window and the
  collapse-on-success redraw

## Blank Environment Integration

Run the host-built tarball integration flow with:

```bash
integration/blank-env/run-host-integration.sh
```

This integration path builds the distribution tarball on the host with
`dzil build`, rebuilds `dd-int-test:latest` from the current
`integration/blank-env/Dockerfile`, runs that container with only the tarball
mounted into it, installs the tarball with `cpanm --notest`, and then
exercises the installed `dashboard` command inside the clean Perl container.
The blank-environment image must also carry the native CPAN build baseline
needed by packaged installs, including `libexpat1-dev`, `libssl-dev`,
`pkg-config`, and `zlib1g-dev`, so modules such as `XML::Parser` and
`Net::SSLeay` can build before the installed-runtime smoke reaches the staged
helper checks.
That image must also provide a real headless Chromium binary instead of an
Ubuntu snap launcher stub, so the browser smoke can execute inside the
container without requiring `snapd`.
The host-side launcher now also runs `prove -lv t/44-smart-router-two-stage.t`
immediately after `dzil build` and before the broader blank-environment
container flow. Treat that smart-router two-stage guard as a managed
post-build gate, not as an optional memory step.

### Post-Build Guard Container Reclaim

`t/44-smart-router-two-stage.t` creates a long-lived container named
`dd-smart-router-two-stage-<pid>-<epoch>` and removes it from an `END` block.
That teardown covers a normal exit and a trapped signal, but not `SIGKILL`, and
every automation round in this repository runs under a `timeout` that kills the
whole process tree when its budget expires. Because the container name embeds a
pid and an epoch that are dead by then, no later run recognised the container as
something it could remove, so a hard-killed run leaked its container
permanently. The guard is not skipped in ordinary runs — it skips only when the
release tarball is absent — so every `prove -lr t` on a host that has built a
tarball could add another leak, and each leak holds a bound loopback port plus a
full dashboard process tree whose in-container processes are root-owned and
therefore out of reach of the host-side stray-collector cleanup.

Better teardown cannot close this. `SIGKILL` cannot be trapped, and the
container is long-lived and exec'd into, so `docker run --rm` does not apply.
The cleanup is therefore generational: before choosing a port or creating its
own container, the guard calls
`Local::DockerGuard::reclaim_guard_containers` (`t/lib/Local/DockerGuard.pm`)
and removes every guard container its predecessors leaked. Reclaiming first also
releases the loopback ports those leaks were holding, which is what keeps the
guard's free-port search from turning intermittent.

A container is judged a leak when the pid embedded in its name no longer
resolves to a running process, or when it is older than one hour. The pid check
collects a fresh leak on the very next run; the age window exists only to
backstop pid recycling, where a dead run's pid has been reissued to an unrelated
live process that would otherwise vouch for the leak forever. A pid that exists
but cannot be signalled from this uid counts as alive, because the leaked
processes run as root and treating "permission denied" as "gone" would be
exactly backwards. Names that do not match the
`dd-smart-router-two-stage-<pid>-<epoch>` shape are never touched, so a sweep
can only ever remove a container this guard created, and the removal is verified
by re-reading the container inventory rather than by trusting `docker rm`'s exit
status — which is non-zero both when removal genuinely failed and when the
container had already gone.

`t/141-smart-router-guard-container-reclaim.t` pins those rules. It drives the
whole decision through an injected command runner and an injected pid probe, so
it needs no docker daemon and runs on any host.

The other docker-driven gates do not have this defect and need no reclaim of
their own: `integration/blank-env/run-host-integration.sh` uses
`docker compose run --rm`, and `integration/windows/run-qemu-windows-smoke.sh`
names its container from a fixed `WINDOWS_DOCKUR_NAME`, which the next run finds
and reuses or replaces by name. Any new gate that creates a long-lived,
uniquely named container must reclaim generationally in the same way.
That blank-container tarball install now assumes the normal `prove -lr t`
suite and explicit numeric `Devel::Cover` gate already passed in the source
tree. Its purpose is packaged dependency resolution and installed-runtime
verification, not rerunning the full tarball test suite a second time.
The release gather rules also exclude local `cover_db` output so a covered
host run does not contaminate the tarball under test.
The release gather rules must also exclude local scratch and dependency trees
such as `node_modules/` and `test_by_michael/`. Those paths are source-tree
implementation details, not distributable runtime assets, so release metadata
must fail before build or release if they are gathered into the tarball.

The shipped runtime-manager lifecycle checks now also fall back to `/proc`
socket ownership scans when that prebuilt image does not include `ss`, and
they re-probe the managed port for late listener pids before restart, so the
integration flow verifies the same stop/restart behavior that a minimal Linux
runtime will see in practice.
Those checks also cover the Starman master-worker split, where the recorded
managed pid can be the master while the bound listener pid is a separate
worker process on the same managed port.
RuntimeManager tests also lock shutdown signal portability by proving the
dashboard lifecycle maps named dashboard intents such as TERM and KILL to
numeric POSIX signals before calling Perl `kill`, matching Alpine/iSH Perl
builds that reject named signal strings.

The integration flow also:

- creates a fake project with its own `./.developer-dashboard` runtime tree
- creates that fake-project runtime tree only after `cpanm` completes, so the tarball's own test phase still runs against a clean runtime
- verifies installed CLI and saved bookmarks from that fake project's local runtime plus config collectors from that same runtime root
- verifies `dashboard version` reports the installed runtime version
- seeds a user-provided fake-project `./.developer-dashboard/cli/update` command plus `update.d` hooks inside the container and verifies `dashboard update` uses the same executable command-hook path as every other top-level subcommand, including late...
- verifies the installed web app denies `/?token=...` browser execution by default while saved bookmark routes still render
- uses headless Chromium to validate the editor, the saved fake-project bookmark page, and the helper login page
- verifies that an installed long-running saved `/ajax/...` route starts streaming visible output within the expected first seconds instead of buffering until process exit
- should be interpreted together with the tracked source-tree integration assets in `doc/integration-test-plan.md`, `doc/windows-testing.md`, and `integration/browser/run-bookmark-browser-smoke.pl`; source-tree tests now fail if those release/support...

## Windows Verification

For Windows-targeted changes, keep the verification layered:

- run the fast forced-Windows unit coverage in `t/`
- run the real Strawberry Perl smoke on a Windows host with `integration/windows/run-strawberry-smoke.ps1`



( run in 0.645 second using v1.01-cache-2.11-cpan-14f38c9f855 )