Add package readiness automation guard

This commit is contained in:
2026-06-05 00:07:36 +02:00
parent db0ecb590c
commit 841fbac8eb
7 changed files with 170 additions and 5 deletions

View File

@@ -74,6 +74,7 @@ ctest --preset desktop-fast-vcpkg --build-config Debug
cmake --preset android-arm64
powershell -ExecutionPolicy Bypass -File scripts\automation\platform-build.ps1 -Presets android-arm64
powershell -ExecutionPolicy Bypass -File scripts\automation\package-smoke.ps1 -Preset windows-msvc-default -Configuration Debug
powershell -ExecutionPolicy Bypass -File scripts\automation\package-smoke.ps1 -ReadinessOnly
cmake --fresh --preset windows-clangcl-asan
python scripts/dev/clangd_nav.py symbols --file src/app_core/brush_ui.h --name execute_brush
python scripts/dev/clangd_nav.py symbols --file src/app_core/brush_ui.h --name-regex "execute_.*preset"
@@ -82,6 +83,7 @@ python scripts/dev/clangd_nav.py definition --file src/node_panel_brush.cpp --li
python scripts/dev/clangd_nav.py references --file src/app_core/brush_ui.h --line 783 --column 45 --path-regex "src[\\/]app_core"
python scripts/dev/clangd_nav.py self-test
python scripts/dev/check_platform_build_targets.py
python scripts/dev/check_package_smoke_readiness.py
```
Known local toolchain state:
@@ -143,6 +145,14 @@ Known local toolchain state:
`panopainter_platform_build_target_matrix_self_test`, verifies the PowerShell
and shell wrapper defaults include every current CMake test executable plus
the required component and `pano_cli` targets.
- `package-smoke.ps1 -ReadinessOnly` and `package-smoke.sh --readiness-only`
emit the Windows AppX, Android standard/Quest/Focus APK, Apple bundle, and
WebGL package readiness matrix without first building an app artifact. The
full package smoke command still builds/checks the selected app target before
reporting the same readiness matrix. `scripts/dev/check_package_smoke_readiness.py`,
registered as `panopainter_package_smoke_readiness_self_test`, verifies both
wrappers keep the same six package kinds, blocked DEBT-0011 status, and
readiness-only mode.
- Root CMake exposes named `fuzz` and `stress` CTest presets. `fuzz` currently
runs deterministic parser/serializer edge tests for binary streams, image
metadata, PPI, stroke scripts, and layout XML; `stress` currently runs the

View File

@@ -1,7 +1,7 @@
# Modernization Debt Log
Status: live
Last updated: 2026-06-04
Last updated: 2026-06-05
Every shortcut, temporary adapter, retained vendored dependency, skipped
platform gate, compatibility shim, or incomplete automation path must be
@@ -26,6 +26,12 @@ agent or engineer to remove them without reconstructing context from chat.
the wrapper defaults against the current CMake test executables, so Android
root CMake validation no longer silently skips newly extracted feature
slices. Package targets remain open under DEBT-0009 and DEBT-0011.
- 2026-06-05: DEBT-0011 was narrowed. `package-smoke.ps1` and
`package-smoke.sh` now have readiness-only modes that report the same
Windows AppX, Android standard/Quest/Focus APK, Apple bundle, and WebGL
blocker matrix without requiring an app build first, and
`panopainter_package_smoke_readiness_self_test` guards package-kind parity
across both wrappers. Package target migration remains open.
- 2026-06-04: DEBT-0036 was narrowed again. Canvas stroke commit,
thumbnail, and object-draw history paths now query saved blend state through
tested `pp_renderer_gl` capability-state dispatch; CanvasLayer equirect

View File

@@ -1263,9 +1263,12 @@ keeps the PowerShell and shell wrapper defaults aligned with every current
CMake test executable plus required component targets.
`package-smoke` now emits a structured package readiness matrix for Windows
AppX, Android standard/Quest/Focus APKs, Apple bundles, and WebGL output, with
blocked prerequisites tied to DEBT-0011. App/package entrypoints still need to
consume shared targets and remain covered by debt until package validation is
migrated from legacy package projects to root CMake.
blocked prerequisites tied to DEBT-0011. It also has a readiness-only mode for
cheap package blocker inventory without building an app artifact, and
`panopainter_package_smoke_readiness_self_test` keeps the PowerShell and shell
readiness matrices aligned. App/package entrypoints still need to consume
shared targets and remain covered by debt until package validation is migrated
from legacy package projects to root CMake.
Implementation tasks:
@@ -1407,6 +1410,7 @@ ctest --preset desktop-fast-vcpkg --build-config Debug
cmake --preset android-arm64
powershell -ExecutionPolicy Bypass -File scripts\automation\platform-build.ps1 -Presets android-arm64
powershell -ExecutionPolicy Bypass -File scripts\automation\package-smoke.ps1 -Preset windows-msvc-default -Configuration Debug
powershell -ExecutionPolicy Bypass -File scripts\automation\package-smoke.ps1 -ReadinessOnly
cmake --fresh --preset windows-clangcl-asan
```
@@ -2036,7 +2040,9 @@ Results:
`data/` copy and report structured package readiness for AppX, Android
standard/Quest/Focus APKs, Apple bundles, and WebGL outputs. Actual package
building remains blocked by DEBT-0011 until those targets are migrated to
root CMake.
root CMake. Readiness-only mode now reports the same matrix without building
the app first, and the package readiness self-test keeps wrapper package
kinds aligned.
- Android arm64 configured with NDK 29.0.14206865 through the platform-build
wrapper and compiled the refreshed headless component/test matrix, including
the current app-core feature-surface automation tests.

View File

@@ -3,6 +3,7 @@ param(
[string]$Preset = "windows-msvc-default",
[string]$Configuration = "Debug",
[string]$Target = "PanoPainter",
[switch]$ReadinessOnly,
[string[]]$PackageKinds = @(
"windows-appx",
"android-standard-apk",
@@ -207,6 +208,21 @@ function Get-PackageReadiness {
return $readiness
}
if ($ReadinessOnly) {
$elapsed = [int]((Get-Date) - $started).TotalMilliseconds
[ordered]@{
command = "package-smoke"
preset = $Preset
configuration = $Configuration
target = $Target
stage = "readiness"
exitCode = 0
elapsedMs = $elapsed
packageReadiness = Get-PackageReadiness -Kinds $PackageKinds
} | ConvertTo-Json -Compress -Depth 5
exit 0
}
& cmake --build --preset $Preset --config $Configuration --target $Target
$buildExitCode = $LASTEXITCODE
if ($buildExitCode -ne 0) {

View File

@@ -5,6 +5,14 @@ preset="${1:-linux-clang}"
configuration="${2:-Debug}"
target="${3:-PanoPainter}"
artifact="${4:-out/build/$preset/$target}"
readiness_only=0
if [ "${1:-}" = "--readiness-only" ]; then
readiness_only=1
preset="${2:-linux-clang}"
configuration="${3:-Debug}"
target="${4:-PanoPainter}"
artifact="${5:-out/build/$preset/$target}"
fi
start="$(date +%s)"
root="$(pwd)"
@@ -84,6 +92,14 @@ package_readiness_json() {
printf ']'
}
if [ "$readiness_only" -eq 1 ]; then
end="$(date +%s)"
elapsed_ms="$(( (end - start) * 1000 ))"
readiness="$(package_readiness_json)"
printf '{"command":"package-smoke","preset":"%s","configuration":"%s","target":"%s","stage":"readiness","exitCode":0,"elapsedMs":%s,"packageReadiness":%s}\n' "$preset" "$configuration" "$target" "$elapsed_ms" "$readiness"
exit 0
fi
cmake --build --preset "$preset" --config "$configuration" --target "$target"
build_exit="$?"
if [ "$build_exit" -ne 0 ]; then

View File

@@ -0,0 +1,106 @@
#!/usr/bin/env python3
"""Verify package-smoke wrappers report the expected package readiness matrix."""
from __future__ import annotations
import json
import re
import sys
from pathlib import Path
EXPECTED_PACKAGE_KINDS = [
"windows-appx",
"android-standard-apk",
"android-quest-apk",
"android-focus-apk",
"apple-bundle",
"webgl",
]
def repo_root() -> Path:
return Path(__file__).resolve().parents[2]
def powershell_package_kinds(root: Path) -> list[str]:
script = (root / "scripts" / "automation" / "package-smoke.ps1").read_text(encoding="utf-8")
match = re.search(r"\[string\[\]\]\$PackageKinds\s*=\s*@\((.*?)\n\s*\)", script, re.S)
if not match:
raise RuntimeError("Could not find PackageKinds default in package-smoke.ps1")
return sorted(re.findall(r'"([^"]+)"', match.group(1)))
def shell_package_kinds(root: Path) -> list[str]:
script = (root / "scripts" / "automation" / "package-smoke.sh").read_text(encoding="utf-8")
return sorted(set(re.findall(r'"kind":"([^"]+)"', script)))
def count_regex(root: Path, patterns: dict[str, str]) -> dict[str, int]:
counts: dict[str, int] = {}
for script_name, pattern in patterns.items():
text = (root / "scripts" / "automation" / script_name).read_text(encoding="utf-8")
counts[script_name] = len(re.findall(pattern, text))
return counts
def main() -> int:
root = repo_root()
expected = sorted(EXPECTED_PACKAGE_KINDS)
ps_kinds = powershell_package_kinds(root)
sh_kinds = shell_package_kinds(root)
debt_counts = count_regex(root, {
"package-smoke.ps1": r'debt\s*=\s*"DEBT-0011"',
"package-smoke.sh": r'"debt":"DEBT-0011"',
})
blocked_counts = count_regex(root, {
"package-smoke.ps1": r'-Status\s+"blocked"',
"package-smoke.sh": r'"status":"blocked"',
})
readiness_mode_counts = {
"package-smoke.ps1": (root / "scripts" / "automation" / "package-smoke.ps1").read_text(encoding="utf-8").count("ReadinessOnly"),
"package-smoke.sh": (root / "scripts" / "automation" / "package-smoke.sh").read_text(encoding="utf-8").count("readiness_only"),
}
missing = {
"package-smoke.ps1": [kind for kind in expected if kind not in ps_kinds],
"package-smoke.sh": [kind for kind in expected if kind not in sh_kinds],
}
unexpected = {
"package-smoke.ps1": [kind for kind in ps_kinds if kind not in expected],
"package-smoke.sh": [kind for kind in sh_kinds if kind not in expected],
}
debt_thresholds = {
"package-smoke.ps1": 1,
"package-smoke.sh": len(expected),
}
debt_complete = {name: count >= debt_thresholds[name] for name, count in debt_counts.items()}
blocked_complete = {name: count >= len(expected) for name, count in blocked_counts.items()}
readiness_mode_present = {name: count > 0 for name, count in readiness_mode_counts.items()}
ok = (
all(not values for values in missing.values())
and all(not values for values in unexpected.values())
and all(debt_complete.values())
and all(blocked_complete.values())
and all(readiness_mode_present.values())
)
print(json.dumps({
"ok": ok,
"expectedPackageKinds": expected,
"packageKinds": {
"package-smoke.ps1": ps_kinds,
"package-smoke.sh": sh_kinds,
},
"missing": missing,
"unexpected": unexpected,
"debtComplete": debt_complete,
"blockedComplete": blocked_complete,
"readinessModePresent": readiness_mode_present,
}, separators=(",", ":")))
return 0 if ok else 1
if __name__ == "__main__":
sys.exit(main())

View File

@@ -10,6 +10,11 @@ add_test(NAME panopainter_platform_build_target_matrix_self_test
set_tests_properties(panopainter_platform_build_target_matrix_self_test PROPERTIES
LABELS "tooling;desktop-fast")
add_test(NAME panopainter_package_smoke_readiness_self_test
COMMAND "${Python3_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/dev/check_package_smoke_readiness.py")
set_tests_properties(panopainter_package_smoke_readiness_self_test PROPERTIES
LABELS "tooling;desktop-fast")
add_library(pp_test_harness INTERFACE)
target_include_directories(pp_test_harness INTERFACE
"${CMAKE_CURRENT_SOURCE_DIR}")