…s saved, and the Windows zip leaves no trace (#2720)
The release smoke checks killed TaskSmack after 5 s, so nothing checked that settings are saved on
exit, and nothing checked that the portable .zip can be deleted without a trace (#2488).
No new hook: TASKSMACK_SCREENSHOT=<png>;after=N (#2827) already gives a normal quit. ScreenshotLayer
writes the frame and calls Window::requestClose(), the title bar Close button's path, so every layer
detaches and ShellLayer::onDetach() saves config.toml; a run that wrote no frame exits non-zero. Its
glReadPixels already runs on Mesa llvmpipe in Windows CI (test_ScreenshotOnDisplay).
- validate-linux-package: each launch (.deb and .tar.gz) gets its own TASKSMACK_CONFIG_DIR and
TASKSMACK_SCREENSHOT after=5, must exit 0 within 60 s, write the frame, and leave a config.toml
whose [theme] table has an id. The asset log checks are unchanged.
- validate-windows-package: the same quit and config.toml check in place of the kill (the wait is
STARTUP_TIMEOUT_SECONDS + ALIVE_SECONDS + 30 s; the step's 5-minute timeout is unchanged), with
the log, llvmpipe and OpenGL 3.3 checks as before. Then leave no trace: TaskSmack-named entries
under %APPDATA% and %LOCALAPPDATA% (case-insensitive, 5 levels deep) are listed before the launch
and after the exit, and any new one outside the config dir and %TEMP% fails; then the folder
TaskSmack ran from is deleted with Remove-Item -ErrorAction Stop, failing on any locked file.
- CONTRIBUTING.md's "Release Artifact Validation" describes the clean quit and the trace check.
Closes #2720
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Description
The release smoke checks (
validate-linux-package,validate-windows-package) killed TaskSmack after 5 s, so nothing checked that the settings are saved on exit, and nothing checked that the portable.zipcan be deleted without leaving anything behind (#2488).No new hook. The issue proposed
TASKSMACK_EXIT_AFTER_FRAMES, butTASKSMACK_SCREENSHOT=<png>;after=N(#2827) already gives a normal quit:ScreenshotLayer::finish()callsWindow::requestClose(), the same path the title bar's Close button takes.Application::run()stops, every layer'sonDetach()runs, andShellLayer::onDetach()callsUserConfig::save().main.cppreturnsEXIT_FAILUREwhen the hook was set and no frame got written, so exit code 0 means the capture ran and the app shut down normally.glReadPixelsalready runs on Mesa llvmpipe in Windows CI (test_ScreenshotOnDisplay, which also assertsconfig.tomlwas saved). The Linux smoke uses SDL'soffscreendriver on an EGL pbuffer, which no CI job has captured from until now (Linux CI runs that test under Xvfb). See Additional Notes.So both smoke steps use
TASKSMACK_SCREENSHOTas the quit.validate-linux-package (
smoke-check.sh, used for both the.deband the.tar.gz):mktemp -dTASKSMACK_CONFIG_DIRandTASKSMACK_SCREENSHOT=$work/smoke.png;after=5, undertimeout 60instead oftimeout 5.config.tomlwhose[theme]table is followed byid = '...'. With no input, nothing writes that file before the shutdown save, so finding it proves the save ran.Assets directory found:, noIcon font not found,Loaded theme:) are unchanged.validate-windows-package ("Smoke-check extracted executable"; same structure, 5-minute step timeout and 15-minute job timeout):
TASKSMACK_SCREENSHOT=<work>\smoke.png;after=$ALIVE_SECONDSis set, and the step waits up toSTARTUP_TIMEOUT_SECONDS + ALIVE_SECONDS + 30s (95 s). It caches$proc.HandlesoExitCodeis still readable after exit. The exit code must be 0. The[critical]/[error],Entering main loop, llvmpipe renderer and OpenGL ≥ 3.3 checks are unchanged. The frame andconfig.tomlwith[theme]must exist.%APPDATA%and%LOCALAPPDATA%whose name matchestasksmack(case-insensitive,-Depth 5, which reachesLocal\Microsoft\Windows\WER\ReportArchive\<report>). Any new entry fails the step unless it is under the config dir or a%TEMP%. That means the private one, or the runner's own temp folder under both itsGetTempPath()spelling and%LOCALAPPDATA%\Temp, since the runner's TEMP can be an 8.3 short path. The step then deletes the folder TaskSmack ran from withRemove-Item -Recurse -Force -ErrorAction Stop, so any locked file fails it (a leftover helper process, for example).The signed x64 leg runs the signed package unchanged. The smoke still runs a temporary copy of the extracted folder with Mesa beside the exe, and that copy is the folder that gets deleted, because it is the one TaskSmack ran from. The untouched
extracted/folder was never run, so deleting it would prove nothing.CONTRIBUTING.md("Release Artifact Validation") now describes the clean quit and the trace check in place of the 5-second timeout.Closes #2720
PR Size
Small: about 160 lines changed in
release.yml(mostly the rewritten Windows smoke step) and 12 inCONTRIBUTING.md.Type of Change
Checklist
clang-tidyand addressed any warnings (no C++ changed)clang-formaton my changes (no C++ changed)pre-commit run --all-filesor installed pre-commit hooks (pre-commit run --fileson both files, actionlint included: all passed)Testing
Both steps' scripts were extracted from
release.ymlwithtests/tools/workflow_steps.pyand dry-run on Windows against fake binaries:smoke-check.sh(Git Bash, withQUIT_TIMEOUT_SECONDScut to 3): a fake that quits 0 with the frame and a[theme]id passes. Each of these fails with its own message: noconfig.toml, no frame, exit 1, a hang (timeout 124),[theme]without anid, and no assets log line.TaskSmack.execompiled from C#): the good run passes and writes the summary. Each of these fails for the right reason: noconfig.toml, exit 1, an[error]log line, a new%LOCALAPPDATA%\TaskSmack-dryrun-leakdirectory (flagged by name), and a child process left with its working directory in the package folder (Remove-Itemreports "being used by another process"). On a dev machine the two AppData scans took about 4 s in all.The real binaries weren't launched: no local release package exists, and no local build has the hook. The end-to-end proof is the next release run, or a dry run of
release.ymldispatched from this branch.Additional Notes
glReadPixelsruns on SDL'soffscreendriver in CI; Linux CI'stest_ScreenshotOnDisplayruns under Xvfb. Reading back from Mesa's EGL pbuffer is expected to work. If it doesn't, the Linux legs fail with "exited with status 1" and aTASKSMACK_SCREENSHOT: ... not writtenlog line, not by hanging. Arelease.ymldry run (workflow_dispatch,dry_run: true) from this branch would confirm it before a release depends on it.backport:0.9(milestone v0.9.1); this only touchesrelease.ymland docs.🤖 Generated with Claude Code