Files
WHC 2a43ae6622 Windows: official port -- UCRT static build, UTF-8 throughout, Low IL sandbox, ncurses TUI, installer; v0.6.0
Build
  * cross build switches to pkgsCross.ucrt64 (shell-win.nix); curl (Schannel),
    winpthreads and ncurses 6.6 are linked statically, so the release is
    cst-agent.exe + busybox.exe (busybox-w32 Unicode) with no extra DLLs
  * windres embeds a manifest with activeCodePage=UTF-8 and a version resource
  * ncurses 6.6 Windows console driver patches (mouse button state, A_DIM,
    wide-char declarations): scripts/patches/
  * scripts/bootstrap_msys2.sh builds the same release natively in MSYS2
    UCRT64; PR #1's MINGW64 notes now point there
  * vendor/tre-regex (musl's TRE) provides <regex.h> on MinGW

UTF-8
  * paths cross the system call boundary as UTF-16 (*W APIs); console code
    pages set to 65001 and restored on exit; files are read and written in
    binary mode
  * child output that is not valid UTF-8 is decoded from the console, OEM or
    ANSI system code page
  * read_file hides CR; edit_file, apply_patch and write_file keep CRLF line
    endings and a UTF-8 BOM

OS layer and processes
  * os_dir_* and cst_fnmatch replace the POSIX-only directory walks in
    subagent, plugins, skills, undo and fs_tools; realpath / path_under and
    symlink rejection work on Windows
  * every child goes through CreateProcessW with its own environment block
    inside a Job Object, so timeouts and Esc end the whole process tree

Sandbox (auto mode, "auto · lowil")
  * run_shell runs with a restricted Low integrity token; the workspace gets
    an inheritable Low label once, the sandbox TEMP is labeled Low, and the
    credentials file gets a no-read-up label
  * drive roots, the profile and system folders are never labeled
  * network commands still ask, since Low IL does not block the network;
    Access denied and MSYS2's 0xc0000142 offer a rerun outside the sandbox
  * /sandbox lists labeled folders, /sandbox reset-labels restores them

TUI
  * raster console fonts are switched to Consolas / NSimSun while running
  * the home logo falls back to text when the console font lacks U+2580
  * mouse drag copy goes to the Windows clipboard

Distribution
  * scripts/install_windows.ps1 installs a Gitea release into
    %LOCALAPPDATA%\Programs\cst-agent and adds it to the user PATH;
    cst-agent-update.cmd reruns it; scripts/release_windows.sh uploads the
    zip with the token taken from the environment
  * scripts/run_windows.bat is gone; docs/README-windows.md is rewritten

Tests
  * new test_lowil, test_win_utf8 and test_os_dir; tests made portable
    (cst_test_util.h)
  * scripts/win_test.sh and scripts/winvm/ run the suite on English and
    Chinese Windows Server VMs as Administrator, a standard user and a
    Chinese user name: 29/29 on all six
2026-10-06 23:08:14 +08:00

66 lines
2.2 KiB
Bash
Executable File

#!/usr/bin/env bash
# Run the cross-built Windows tests inside the test VMs (scripts/winvm/vm.sh).
#
# scripts/win_test.sh [-b BUILD_DIR] [-v "en zh"] [-u "Administrator tester 测试"] [-f FILTER]
#
# BUILD_DIR (default build-win) must contain test_*.exe from the MinGW build;
# busybox.exe is taken from third_party/busybox/. VMs that are not running are
# started and stopped again afterwards. Exit status is the number of failed
# variant/user runs.
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
VM="$ROOT/scripts/winvm/vm.sh"
BUILD="$ROOT/build-win"
VARIANTS="en zh"
USERS_LIST="Administrator tester 测试"
FILTER="*"
while getopts "b:v:u:f:" o; do
case "$o" in
b) BUILD="$(cd "$OPTARG" && pwd)" ;;
v) VARIANTS="$OPTARG" ;;
u) USERS_LIST="$OPTARG" ;;
f) FILTER="$OPTARG" ;;
*) sed -n '2,8p' "$0"; exit 2 ;;
esac
done
shopt -s nullglob
exes=("$BUILD"/test_*.exe)
[[ ${#exes[@]} -gt 0 ]] || { echo "no test_*.exe in $BUILD" >&2; exit 1; }
stage="$(mktemp -d)"
trap 'rm -rf "$stage"' EXIT
mkdir -p "$stage/bundle/bin" "$stage/bundle/data/tests"
cp "${exes[@]}" "$stage/bundle/bin/"
for f in "$BUILD"/*.dll "$ROOT/third_party/busybox/busybox.exe"; do
[[ -f "$f" ]] && cp "$f" "$stage/bundle/bin/"
done
cp -r "$ROOT/tests/fixtures" "$stage/bundle/data/tests/"
cp "$ROOT/scripts/winvm/run_tests.ps1" "$stage/bundle/"
tar -C "$stage" -cf "$stage/bundle.tar" bundle
total_fail=0
summary=()
for v in $VARIANTS; do
started=0
if [[ "$("$VM" status "$v")" != running ]]; then
"$VM" start "$v"
started=1
fi
"$VM" ssh "$v" 'Remove-Item -Recurse -Force C:\cst-test\bundle -ErrorAction SilentlyContinue; New-Item -ItemType Directory -Force C:\cst-test | Out-Null' >/dev/null
"$VM" put "$v" "$stage/bundle.tar" "C:/cst-test/bundle.tar"
"$VM" ssh "$v" 'tar -xf C:\cst-test\bundle.tar -C C:\cst-test; Remove-Item C:\cst-test\bundle.tar' >/dev/null
for u in $USERS_LIST; do
echo "===== $v / $u"
rc=0
"$VM" ps "$v" "$u" "$stage/bundle/run_tests.ps1" -Bundle 'C:\cst-test\bundle' -Filter "'$FILTER'" || rc=$?
summary+=("$v/$u: $([[ $rc == 0 ]] && echo ok || echo FAIL)")
[[ $rc == 0 ]] || total_fail=$((total_fail + 1))
done
[[ $started == 1 ]] && "$VM" stop "$v"
done
echo
printf '%s\n' "${summary[@]}"
exit "$total_fail"