Fix macOS CI compatibility and isolation cleanup
This commit is contained in:
1 parent
92e901f844
commit
904a576a22
6 files changed
+143
-33
No files matched your search
@@ -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
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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" \
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
Reference in new issue
Block a user