Fix macOS CI compatibility and isolation cleanup

This commit is contained in:
gd-ops committed 2026-07-16 12:24:27 +08:00
1 parent 92e901f844
commit 904a576a22
6 files changed
+143 -33

No files matched your search

+2 -2
View File
@@ -51,12 +51,12 @@ jobs:
~/.cargo/git
target
apps/macos/.build
key: ${{ runner.os }}-${{ runner.arch }}-apc-${{ hashFiles('rust-toolchain.toml', 'Cargo.lock', 'apps/macos/Package.swift') }}
key: ${{ runner.os }}-${{ runner.arch }}-apc-${{ hashFiles('rust-toolchain.toml', 'Cargo.lock', 'apps/macos/Package.swift', 'apps/macos/Package.resolved') }}
restore-keys: |
${{ runner.os }}-${{ runner.arch }}-apc-
- name: Validate default test isolation
run: ./script/validate_test_isolation.sh
run: TMPDIR="$RUNNER_TEMP/" /bin/bash ./script/validate_test_isolation.sh
- name: Check Rust formatting
run: cargo fmt --all -- --check
+5 -5
View File
@@ -1,13 +1,13 @@
{
"originHash" : "3f4988a45a3ec26c7d6457c6cc4a46311c1d6f86fd7480ac7872cd2c61c5422c",
"originHash" : "f0239a2e37b572ca81f9f03b92018d20b34ec51c73788a076206d192fcc84025",
"pins" : [
{
"identity" : "swift-syntax",
"kind" : "remoteSourceControl",
"location" : "https://github.com/swiftlang/swift-syntax.git",
"state" : {
"revision" : "79e4b74a295b6eb74a8b585e3a39d29e70c1dbd1",
"version" : "603.0.2"
"revision" : "f99ae8aa18f0cf0d53481901f88a0991dc3bd4a2",
"version" : "601.0.1"
}
},
{
@@ -15,8 +15,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/swiftlang/swift-testing.git",
"state" : {
"revision" : "70eff261d7f462cad1fff51e05bcc74aa0b0f420",
"version" : "6.3.2"
"revision" : "1d1f7e489c9c606ae6c2caa10385372736ef4a39",
"version" : "6.1.3"
}
}
],
+4 -1
View File
@@ -22,7 +22,10 @@ let package = Package(
)
],
dependencies: [
.package(url: "https://github.com/swiftlang/swift-testing.git", exact: "6.3.2")
// Keep the test framework compatible with the Swift 6.1 toolchain on
// the macOS 15 CI runner. Newer 6.2/6.3 tags raise their own manifest
// tools version and cannot even be resolved by that baseline.
.package(url: "https://github.com/swiftlang/swift-testing.git", exact: "6.1.3")
],
targets: [
.target(name: "AgentPetCompanionCore"),
+3 -2
View File
@@ -29,7 +29,7 @@ App、PetCore、CLI 和连接器不再只比较 RPC 版本。每个 bundle 携
健康版本提交后,`APC_HOME/runtime/current` 以原子符号链接指向当前完整 runtime。已安装连接器统一引用稳定的 `runtime/current/petcore-cli`,App 在版本交接后只刷新已经安装的本项目 connector 文件/配置项;未安装连接器和用户的其他配置不改写。连接器因此不再随 build ID 固化到旧 CLI。
`APC_DISABLE_LAUNCH_AGENT=1` 只影响本次隔离验证,绝不会 bootout 用户全局 `dev.agentpet.petcore`。验证清理会按隔离 `APC_HOME/runtime/versions` 下的精确可执行路径认领被 reparent 的 PetCore,并进行 TERM/KILL 有界回收。
`APC_DISABLE_LAUNCH_AGENT=1` 只影响本次隔离验证,绝不会 bootout 用户全局 `dev.agentpet.petcore`。验证清理会按隔离 `APC_HOME/runtime/versions` 下的精确可执行路径认领被 reparent 的 PetCore,并进行 TERM/KILL 有界回收。CI 明确用 macOS 系统 Bash 3.2 和尾斜杠 `TMPDIR` 执行该门禁;路径归一化兼容 Bash 3.2/5,身份协议存在但校验失败时禁止退化为路径扫描,夹具失败也会按本次记录的精确 PID 回收,不遗留孤儿进程。
## 3. 产品与 UI 完成度
@@ -92,8 +92,9 @@ V1 仍不包含公共素材库、分享/社区、Petdex 导入、Codex 内置宠
| 验证 | 结果 |
|---|---:|
| `script/test_all.sh` 默认 deterministic/simulated/security/bounded-stress 门禁 | PASS |
| Swift tests | PASS,96 tests / 10 suites(含玻璃辅助功能回退与旧 `apc.runtime-manifest.v1` 新增字段重建回归) |
| Swift tests | PASS,96 tests / 10 suites(含玻璃辅助功能回退与旧 `apc.runtime-manifest.v1` 新增字段重建回归);`swift-testing` 固定为兼容 CI Swift 6.1.2 的 6.1.3,直接 `swift test` 与本地兼容验证入口均通过。 |
| Rust workspace tests、fmt、clippy `-D warnings` | PASS |
| 默认测试隔离与 owned-runtime 回收 | PASS:系统 Bash 3.2、Bash 5、尾斜杠/含空格临时路径和真实 reparent 场景均通过;篡改身份时 fail closed,无夹具进程残留。 |
| App lifecycle contract | PASS,8/8 |
| Swift overlay/UI offline contract | PASS,10/10 |
| develop App bundle + runtime manifest + connector repair smoke | PASS |
+91 -10
View File
@@ -8,7 +8,8 @@ if ! command -v rg >/dev/null 2>&1; then
exit 2
fi
TMP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/apc-test-isolation.XXXXXX")"
TMP_ROOT="${TMPDIR:-/tmp}"
TMP_DIR="$(mktemp -d "${TMP_ROOT%/}/apc-test-isolation.XXXXXX")"
SHIM_DIR="$TMP_DIR/shims"
FORBIDDEN_LOG="$TMP_DIR/forbidden.log"
FAILURES=0
@@ -359,9 +360,46 @@ else
' *) printf '\''{"ok":true}\n'\'' ;;' \
'esac'
runtime_fixture_status=0
set +e
(
set -e
apc_use_isolated_home "$RUNTIME_HOME"
export APC_FAKE_PETCORE="$RUNTIME_PETCORE"
first_app_pid=""
first_petcore_pid=""
second_app_pid=""
second_petcore_pid=""
managed_petcore_pid=""
runtime_fixture_cleanup_pid() {
local fixture_pid="${1:-}"
local fixture_command=""
[[ "$fixture_pid" =~ ^[0-9]+$ ]] || return 0
fixture_command="$(ps -p "$fixture_pid" -o command= 2>/dev/null || true)"
[[ -n "$fixture_command" && "$fixture_command" == *"$RUNTIME_FIXTURE"* ]] || return 0
kill "$fixture_pid" >/dev/null 2>&1 || true
for _ in {1..50}; do
kill -0 "$fixture_pid" >/dev/null 2>&1 || break
sleep 0.02
done
if kill -0 "$fixture_pid" >/dev/null 2>&1; then
kill -KILL "$fixture_pid" >/dev/null 2>&1 || true
for _ in {1..50}; do
kill -0 "$fixture_pid" >/dev/null 2>&1 || break
sleep 0.02
done
fi
wait "$fixture_pid" >/dev/null 2>&1 || true
}
runtime_fixture_cleanup() {
runtime_fixture_cleanup_pid "$managed_petcore_pid"
runtime_fixture_cleanup_pid "$second_petcore_pid"
runtime_fixture_cleanup_pid "$second_app_pid"
runtime_fixture_cleanup_pid "$first_petcore_pid"
runtime_fixture_cleanup_pid "$first_app_pid"
}
trap runtime_fixture_cleanup EXIT
apc_start_owned_runtime "$RUNTIME_APP" "$RUNTIME_CLI" "$RUNTIME_PETCORE" "$RUNTIME_FIXTURE/app.log" "$RUNTIME_PROTOCOL"
first_app_pid="$APC_OWNED_APP_PID"
first_petcore_pid="$APC_OWNED_PETCORE_PID"
@@ -378,9 +416,20 @@ assert data["instance_id"] == "fixture-instance", data
assert data["process_start"], data
PY
apc_stop_owned_runtime "$RUNTIME_CLI" "$RUNTIME_PETCORE" "$RUNTIME_PROTOCOL"
! kill -0 "$first_app_pid" >/dev/null 2>&1
! kill -0 "$first_petcore_pid" >/dev/null 2>&1
if kill -0 "$first_app_pid" >/dev/null 2>&1; then
printf 'owned runtime fixture left its first App running\n' >&2
exit 1
fi
if kill -0 "$first_petcore_pid" >/dev/null 2>&1; then
printf 'owned runtime fixture left its first PetCore running\n' >&2
exit 1
fi
TAMPERED_MANAGED_PETCORE="$APC_HOME/runtime/versions/tampered/petcore"
mkdir -p "$(dirname "$TAMPERED_MANAGED_PETCORE")"
cp "$RUNTIME_PETCORE" "$TAMPERED_MANAGED_PETCORE"
chmod +x "$TAMPERED_MANAGED_PETCORE"
export APC_FAKE_PETCORE="$TAMPERED_MANAGED_PETCORE"
apc_start_owned_runtime "$RUNTIME_APP" "$RUNTIME_CLI" "$RUNTIME_PETCORE" "$RUNTIME_FIXTURE/app-tampered.log" "$RUNTIME_PROTOCOL"
second_app_pid="$APC_OWNED_APP_PID"
second_petcore_pid="$APC_OWNED_PETCORE_PID"
@@ -393,29 +442,61 @@ with open(path, "w", encoding="utf-8") as file:
json.dump(data, file)
PY
apc_stop_owned_runtime "$RUNTIME_CLI" "$RUNTIME_PETCORE" "$RUNTIME_PROTOCOL"
! kill -0 "$second_app_pid" >/dev/null 2>&1
if kill -0 "$second_app_pid" >/dev/null 2>&1; then
printf 'owned runtime fixture left its second App running\n' >&2
exit 1
fi
kill -0 "$second_petcore_pid" >/dev/null 2>&1
kill "$second_petcore_pid" >/dev/null 2>&1 || true
runtime_fixture_cleanup_pid "$second_petcore_pid"
if kill -0 "$second_petcore_pid" >/dev/null 2>&1; then
printf 'owned runtime fixture could not reap its preserved PetCore\n' >&2
exit 1
fi
# A Process child is reparented after the App exits. Cleanup must still
# reclaim a daemon whose executable is inside this isolated runtime tree,
# even when no owner protocol could be published.
MANAGED_PETCORE="$APC_HOME/runtime/versions/fixture/petcore"
# Keep a deliberate duplicate separator in the launched command. macOS
# system Bash 3.2 must normalize it exactly as Bash 5 does.
MANAGED_PETCORE="$APC_HOME/runtime//versions/fixture/petcore"
MANAGED_LAUNCHER="$RUNTIME_FIXTURE/managed-launcher"
MANAGED_PID_FILE="$RUNTIME_FIXTURE/managed-pids"
mkdir -p "$(dirname "$MANAGED_PETCORE")"
write_executable "$MANAGED_PETCORE" \
'#!/usr/bin/env bash' \
'trap '\''exit 0'\'' TERM INT' \
'while :; do sleep 1; done'
"$MANAGED_PETCORE" serve --home "$APC_HOME" &
managed_petcore_pid="$!"
write_executable "$MANAGED_LAUNCHER" \
'#!/usr/bin/env bash' \
'set -euo pipefail' \
'"$APC_MANAGED_PETCORE" serve --home "$APC_HOME" &' \
'printf '\''%s\t%s\n'\'' "$$" "$!" >"$APC_MANAGED_PID_FILE"'
export APC_MANAGED_PETCORE="$MANAGED_PETCORE"
export APC_MANAGED_PID_FILE="$MANAGED_PID_FILE"
"$MANAGED_LAUNCHER"
IFS=$'\t' read -r managed_launcher_pid managed_petcore_pid <"$MANAGED_PID_FILE"
kill -0 "$managed_petcore_pid" >/dev/null 2>&1
managed_parent_pid="$(ps -p "$managed_petcore_pid" -o ppid= 2>/dev/null | tr -d ' ' || true)"
if [[ -z "$managed_parent_pid" || "$managed_parent_pid" == "$managed_launcher_pid" ]]; then
printf 'owned runtime fixture did not reparent its managed PetCore\n' >&2
exit 1
fi
APC_OWNED_APP_PID=""
APC_OWNED_PETCORE_PID=""
APC_OWNED_PROCESS_START=""
APC_OWNED_INSTANCE_ID=""
APC_OWNED_APP_BINARY=""
apc_stop_owned_runtime "$RUNTIME_CLI" "$RUNTIME_PETCORE" "$RUNTIME_PROTOCOL"
! kill -0 "$managed_petcore_pid" >/dev/null 2>&1
) || record_failure 'owned runtime fixture failed PID/marker/snapshot initialization or conservative cleanup'
if kill -0 "$managed_petcore_pid" >/dev/null 2>&1; then
printf 'owned runtime fixture left its protocol-free managed PetCore running\n' >&2
exit 1
fi
)
runtime_fixture_status="$?"
set -e
if [[ "$runtime_fixture_status" != "0" ]]; then
record_failure 'owned runtime fixture failed PID/marker/snapshot initialization or conservative cleanup'
fi
fi
for validator in "${DEFAULT_VALIDATORS[@]}"; do
if rg -q 'petcore" serve' "$validator" \
+38 -13
View File
@@ -248,6 +248,15 @@ apc_stop_owned_runtime() {
local protocol_path="$3"
local may_stop_petcore=0
local owned_child_pid=""
local allow_managed_fallback=0
# A present protocol is an ownership claim. If its live identity no longer
# validates, fail closed instead of replacing that claim with a path scan.
# The bounded managed-runtime fallback is only for startup failures that
# occurred before any protocol could be published.
if [[ ! -f "$protocol_path" ]]; then
allow_managed_fallback=1
fi
if [[ -z "$APC_OWNED_APP_PID" && -f "$protocol_path" ]]; then
apc_claim_owned_runtime "$petcore_cli" "$petcore_binary" "$protocol_path" || true
@@ -275,26 +284,35 @@ apc_stop_owned_runtime() {
# APC_HOME/runtime/versions tree; this cannot match the user's global
# Application Support runtime or an unrelated PetCore.
local candidate_pid candidate_command managed_runtime_prefix
local duplicate_separator="//"
local single_separator="/"
managed_runtime_prefix="$(python3 - "$APC_HOME" <<'PY'
import os
import sys
print(os.path.normpath(os.path.join(sys.argv[1], "runtime", "versions")))
PY
)"
while read -r candidate_pid candidate_command; do
[[ "$candidate_pid" =~ ^[0-9]+$ ]] || continue
while [[ "$candidate_command" == *"//"* ]]; do
candidate_command="${candidate_command//\/\//\/}"
done
if [[ "$candidate_command" == *"$managed_runtime_prefix"/*"/petcore serve "* ]]; then
owned_child_pid="$candidate_pid"
break
fi
done < <(ps -axo pid=,command= 2>/dev/null || true)
if [[ "$allow_managed_fallback" == "1" ]]; then
while read -r candidate_pid candidate_command; do
[[ "$candidate_pid" =~ ^[0-9]+$ ]] || continue
# macOS still ships Bash 3.2. In that version, spelling the slash pattern
# and replacement inline turns the replacement into a literal `\/`.
# Variables keep the same normalization compatible with Bash 3.2 and 5.
while [[ "$candidate_command" == *"$duplicate_separator"* ]]; do
candidate_command="${candidate_command//$duplicate_separator/$single_separator}"
done
if [[ "$candidate_command" == *"$managed_runtime_prefix"/*"/petcore serve "* ]]; then
owned_child_pid="$candidate_pid"
break
fi
done < <(ps -axo pid=,command= 2>/dev/null || true)
fi
if [[ -z "$owned_child_pid" && -n "$APC_OWNED_APP_PID" ]]; then
# Retain the parent-scoped fallback for test fixtures that use a synthetic
# PetCore command outside the staged runtime tree.
if [[ "$allow_managed_fallback" == "1" \
&& -z "$owned_child_pid" \
&& -n "$APC_OWNED_APP_PID" ]]; then
# Retain the parent-scoped fallback for a managed PetCore that has not yet
# published an ownership protocol.
managed_runtime_prefix="$(python3 - "$APC_HOME" <<'PY'
import os
import sys
@@ -304,6 +322,9 @@ PY
while IFS= read -r candidate_pid; do
[[ "$candidate_pid" =~ ^[0-9]+$ ]] || continue
candidate_command="$(ps -p "$candidate_pid" -o command= 2>/dev/null || true)"
while [[ "$candidate_command" == *"$duplicate_separator"* ]]; do
candidate_command="${candidate_command//$duplicate_separator/$single_separator}"
done
if [[ "$candidate_command" == "$managed_runtime_prefix"/*"/petcore serve "* ]]; then
owned_child_pid="$candidate_pid"
break
@@ -334,6 +355,10 @@ PY
if kill -0 "$owned_child_pid" >/dev/null 2>&1; then
kill -KILL "$owned_child_pid" >/dev/null 2>&1 || true
fi
# This is a direct child in the synthetic fixture and a reparented process
# in the real App case. wait reaps the former and is a harmless no-op for
# the latter, keeping Bash 3.2 from emitting a stale job notification.
wait "$owned_child_pid" >/dev/null 2>&1 || true
fi
rm -f "$protocol_path"