306 lines
11 KiB
Shell
Executable file
306 lines
11 KiB
Shell
Executable file
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
expected_machine_id="caa7b1019517470f9d1368b6e79db49e"
|
|
expected_source_commit="${1:-}"
|
|
release_root="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
source_commit_file="${release_root}/SOURCE-COMMIT"
|
|
state_root="/var/lib/guanghu/personas/guanghu/hlcc-v16.0.1"
|
|
repository_root="${state_root}/data/repositories/bingshuo"
|
|
runtime_root="/var/lib/guanghu/personas/guanghu/fifth-domain-daily"
|
|
receipt_root="/var/lib/guanghu/architecture-provision/receipts"
|
|
receipt="${receipt_root}/JD-PERSONA-FIFTH-DOMAIN-DAILY-20260806.json"
|
|
archive_root="/var/lib/guanghu/legacy-archives/persona-writers/20260806-fifth-domain-daily"
|
|
shared_secret_file="/etc/guanghu/persona-secrets/shared-deepseek.env"
|
|
temporary="$(mktemp -d)"
|
|
chmod 0711 "$temporary"
|
|
backup_root="${temporary}/units"
|
|
completed=0
|
|
|
|
writers=(
|
|
chenglu-agent.service
|
|
chenglu-daily.timer
|
|
guideng-agent.service
|
|
kezhou-daily.timer
|
|
)
|
|
oneshots=(
|
|
chenglu-daily.service
|
|
kezhou-daily.service
|
|
)
|
|
handshakes=(
|
|
chenglu-team-handshake.service
|
|
guideng-team-handshake.service
|
|
kezhou-team-handshake.service
|
|
)
|
|
new_units=(
|
|
chenglu-team-handshake.service
|
|
guideng-team-handshake.service
|
|
kezhou-team-handshake.service
|
|
zhuyuan-persona-team-session.target
|
|
zhuyuan-persona-fifth-domain-daily.service
|
|
zhuyuan-persona-fifth-domain-daily.timer
|
|
)
|
|
|
|
declare -A old_repositories=(
|
|
[chenglu-agent]="/var/lib/guanghu/forgejo/repositories/bingshuo/chenglu-agent.git"
|
|
[guideng]="/var/lib/guanghu/forgejo/repositories/bingshuo/guideng.git"
|
|
[kezhou]="/var/lib/guanghu/forgejo/repositories/bingshuo/kezhou.git"
|
|
)
|
|
|
|
restore_units() {
|
|
for unit in "${new_units[@]}"; do
|
|
if test -f "${backup_root}/${unit}"; then
|
|
cp "${backup_root}/${unit}" "/etc/systemd/system/${unit}"
|
|
else
|
|
rm -f "/etc/systemd/system/${unit}"
|
|
fi
|
|
done
|
|
systemctl daemon-reload
|
|
}
|
|
|
|
rollback() {
|
|
systemctl disable --now zhuyuan-persona-fifth-domain-daily.timer \
|
|
>/dev/null 2>&1 || true
|
|
systemctl stop zhuyuan-persona-fifth-domain-daily.service \
|
|
>/dev/null 2>&1 || true
|
|
restore_units
|
|
if test -f "${temporary}/shared-deepseek.env"; then
|
|
cp "${temporary}/shared-deepseek.env" "$shared_secret_file"
|
|
chmod 0600 "$shared_secret_file"
|
|
fi
|
|
systemctl enable --now guanghu-forgejo.service >/dev/null 2>&1 || true
|
|
for unit in "${writers[@]}"; do
|
|
systemctl enable --now "$unit" >/dev/null 2>&1 || true
|
|
done
|
|
for unit in "${handshakes[@]}"; do
|
|
systemctl restart "$unit" >/dev/null 2>&1 || true
|
|
done
|
|
}
|
|
|
|
on_exit() {
|
|
status=$?
|
|
trap - EXIT
|
|
if test "$completed" = 0; then
|
|
rollback
|
|
fi
|
|
rm -rf "$temporary"
|
|
exit "$status"
|
|
}
|
|
trap on_exit EXIT
|
|
|
|
test "$(id -u)" = "0"
|
|
test "$(cat /etc/machine-id)" = "$expected_machine_id"
|
|
test -n "$expected_source_commit"
|
|
test "$expected_source_commit" = "$(tr -d '\n' <"$source_commit_file")"
|
|
test "$(basename "$release_root")" = "$expected_source_commit"
|
|
test -f "${release_root}/server-tools/persona-team-handshake/fifth-domain-daily-orchestrator.mjs"
|
|
test -f "${release_root}/server-tools/persona-team-handshake/fifth-domain-daily.config.server.json"
|
|
test -f "$shared_secret_file"
|
|
test "$(systemctl is-active hlcc-jd-candidate.service)" = "active"
|
|
test "$(curl -fsS http://127.0.0.1:3341/health | jq -r .ready)" = "true"
|
|
|
|
mkdir -p "$backup_root" "$archive_root" "$receipt_root" "$runtime_root"
|
|
cp "$shared_secret_file" "${temporary}/shared-deepseek.env"
|
|
chmod 0600 "${temporary}/shared-deepseek.env"
|
|
if ! grep -q '^PERSONA_TEAM_CONTROLLER_TOKEN=' "$shared_secret_file"; then
|
|
controller_token="$(openssl rand -hex 32)"
|
|
test "${#controller_token}" = "64"
|
|
printf '\nPERSONA_TEAM_CONTROLLER_TOKEN=%s\n' "$controller_token" \
|
|
>>"$shared_secret_file"
|
|
chmod 0600 "$shared_secret_file"
|
|
fi
|
|
chown guanghu:guanghu "$runtime_root"
|
|
chmod 0700 "$runtime_root"
|
|
for unit in "${new_units[@]}"; do
|
|
if test -f "/etc/systemd/system/${unit}"; then
|
|
cp "/etc/systemd/system/${unit}" "${backup_root}/${unit}"
|
|
fi
|
|
done
|
|
for unit in "${writers[@]}" "${oneshots[@]}" guanghu-forgejo.service; do
|
|
systemctl cat "$unit" >"${archive_root}/${unit}.txt" 2>&1 || true
|
|
done
|
|
|
|
source_paths=()
|
|
for candidate in /opt/chenglu-agent /opt/guideng/agent /opt/kezhou/agent; do
|
|
if test -e "$candidate"; then
|
|
source_paths+=("$candidate")
|
|
fi
|
|
done
|
|
if test "${#source_paths[@]}" -gt 0; then
|
|
tar -czf "${archive_root}/legacy-persona-writer-source.tar.gz" \
|
|
"${source_paths[@]}"
|
|
sha256sum "${archive_root}/legacy-persona-writer-source.tar.gz" \
|
|
>"${archive_root}/legacy-persona-writer-source.tar.gz.sha256"
|
|
fi
|
|
|
|
for name in chenglu-agent guideng kezhou; do
|
|
test -d "${old_repositories[$name]}"
|
|
test -d "${repository_root}/${name}.git"
|
|
git --git-dir="${old_repositories[$name]}" \
|
|
fsck --connectivity-only --no-dangling
|
|
git --git-dir="${repository_root}/${name}.git" \
|
|
fsck --connectivity-only --no-dangling
|
|
done
|
|
|
|
for unit in "${writers[@]}"; do
|
|
systemctl disable --now "$unit"
|
|
done
|
|
for unit in "${oneshots[@]}"; do
|
|
systemctl stop "$unit" || true
|
|
done
|
|
for unit in "${writers[@]}" "${oneshots[@]}"; do
|
|
test "$(systemctl is-active "$unit" || true)" != "active"
|
|
done
|
|
|
|
for name in chenglu-agent guideng kezhou; do
|
|
bundle="${temporary}/${name}.bundle"
|
|
git --git-dir="${old_repositories[$name]}" bundle create "$bundle" \
|
|
--branches --tags
|
|
chown guanghu:guanghu "$bundle"
|
|
runuser -u guanghu -- git --git-dir="${repository_root}/${name}.git" \
|
|
fetch "$bundle" \
|
|
"+refs/heads/*:refs/heads/*" \
|
|
"+refs/tags/*:refs/tags/*"
|
|
old_refs="$(
|
|
git --git-dir="${old_repositories[$name]}" for-each-ref \
|
|
--format='%(refname) %(objectname)' refs/heads refs/tags |
|
|
sort
|
|
)"
|
|
new_refs="$(
|
|
git --git-dir="${repository_root}/${name}.git" for-each-ref \
|
|
--format='%(refname) %(objectname)' refs/heads refs/tags |
|
|
sort
|
|
)"
|
|
test "$new_refs" = "$old_refs"
|
|
done
|
|
|
|
for unit in "${handshakes[@]}"; do
|
|
sed "s|__RELEASE_ROOT__|${release_root}|g" \
|
|
"${release_root}/server-tools/persona-team-handshake/${unit}" \
|
|
>"/etc/systemd/system/${unit}"
|
|
done
|
|
for unit in \
|
|
zhuyuan-persona-team-session.target \
|
|
zhuyuan-persona-fifth-domain-daily.service \
|
|
zhuyuan-persona-fifth-domain-daily.timer; do
|
|
sed "s|__RELEASE_ROOT__|${release_root}|g" \
|
|
"${release_root}/server-tools/persona-team-handshake/${unit}" \
|
|
>"/etc/systemd/system/${unit}"
|
|
done
|
|
|
|
systemctl daemon-reload
|
|
systemctl disable "${handshakes[@]}" >/dev/null 2>&1 || true
|
|
systemctl stop zhuyuan-persona-team-session.target "${handshakes[@]}" \
|
|
>/dev/null 2>&1 || true
|
|
systemctl start zhuyuan-persona-team-session.target
|
|
for port in 3932 3933 3934; do
|
|
ready=0
|
|
for attempt in $(seq 1 30); do
|
|
if test "$(curl -fsS "http://127.0.0.1:${port}/health" 2>/dev/null | jq -r .ok 2>/dev/null)" = "true"; then
|
|
ready=1
|
|
break
|
|
fi
|
|
sleep 1
|
|
done
|
|
test "$ready" = 1
|
|
done
|
|
|
|
before_chenglu="$(git --git-dir="${repository_root}/chenglu-agent.git" rev-parse main)"
|
|
before_guideng="$(git --git-dir="${repository_root}/guideng.git" rev-parse main)"
|
|
before_kezhou="$(git --git-dir="${repository_root}/kezhou.git" rev-parse main)"
|
|
|
|
systemctl enable --now zhuyuan-persona-fifth-domain-daily.timer
|
|
systemctl start zhuyuan-persona-fifth-domain-daily.service
|
|
test "$(systemctl is-active zhuyuan-persona-fifth-domain-daily.timer)" = "active"
|
|
systemctl stop zhuyuan-persona-team-session.target
|
|
for unit in "${handshakes[@]}"; do
|
|
test "$(systemctl is-active "$unit" || true)" != "active"
|
|
done
|
|
|
|
after_chenglu="$(git --git-dir="${repository_root}/chenglu-agent.git" rev-parse main)"
|
|
after_guideng="$(git --git-dir="${repository_root}/guideng.git" rev-parse main)"
|
|
after_kezhou="$(git --git-dir="${repository_root}/kezhou.git" rev-parse main)"
|
|
test "$after_chenglu" != "$before_chenglu"
|
|
test "$after_guideng" != "$before_guideng"
|
|
test "$after_kezhou" != "$before_kezhou"
|
|
source_sha="$(git --git-dir="${repository_root}/guanghu-ice-heart.git" rev-parse main)"
|
|
for name in chenglu-agent guideng kezhou; do
|
|
observed="$(
|
|
git --git-dir="${repository_root}/${name}.git" \
|
|
show main:persona-system/fifth-domain/CURRENT.json |
|
|
jq -r .last_source_sha
|
|
)"
|
|
test "$observed" = "$source_sha"
|
|
runuser -u guanghu -- git \
|
|
-c "safe.directory=${repository_root}/${name}.git" \
|
|
--git-dir="${repository_root}/${name}.git" \
|
|
fsck --connectivity-only --no-dangling
|
|
done
|
|
|
|
systemctl disable --now guanghu-forgejo.service
|
|
test "$(systemctl is-active guanghu-forgejo.service || true)" != "active"
|
|
if ss -ltnH | awk '{print $4}' | grep -Eq '(^|:)3001$'; then
|
|
exit 41
|
|
fi
|
|
test "$(curl -fsS http://127.0.0.1:3341/health | jq -r .ready)" = "true"
|
|
test "$(systemctl is-active zhuyuan-persona-team-session.target || true)" != "active"
|
|
|
|
latest_daily_receipt="$(
|
|
find "${runtime_root}/receipts" -maxdepth 1 -type f \
|
|
-name 'ZY-PERSONA-DAILY-*.json' -printf '%T@ %p\n' |
|
|
sort -nr |
|
|
head -n 1 |
|
|
cut -d' ' -f2-
|
|
)"
|
|
test -n "$latest_daily_receipt"
|
|
test "$(jq -r .result "$latest_daily_receipt")" = "PASS"
|
|
test "$(jq '[.results[] | select(.status == "FIFTH_DOMAIN_UPDATE_COMMITTED")] | length' "$latest_daily_receipt")" = "3"
|
|
|
|
jq -n \
|
|
--arg completed_at "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
|
--arg source_commit "$expected_source_commit" \
|
|
--arg source_sha "$source_sha" \
|
|
--arg before_chenglu "$before_chenglu" \
|
|
--arg after_chenglu "$after_chenglu" \
|
|
--arg before_guideng "$before_guideng" \
|
|
--arg after_guideng "$after_guideng" \
|
|
--arg before_kezhou "$before_kezhou" \
|
|
--arg after_kezhou "$after_kezhou" \
|
|
--arg daily_receipt "$latest_daily_receipt" \
|
|
--arg source_archive_sha "$(
|
|
awk '{print $1}' "${archive_root}/legacy-persona-writer-source.tar.gz.sha256" \
|
|
2>/dev/null || printf 'NO_SOURCE_ARCHIVE'
|
|
)" \
|
|
'{
|
|
schema: "guanghu.persona-fifth-domain-daily-deployment-receipt/v1",
|
|
receipt_id: "JD-PERSONA-FIFTH-DOMAIN-DAILY-20260806",
|
|
target_node: "JD-FD-PRIMARY",
|
|
completed_at: $completed_at,
|
|
source_commit: $source_commit,
|
|
source_fifth_domain_sha: $source_sha,
|
|
result: "THREE_ROLE_PERSONA_SYSTEMS_DAILY_FIFTH_DOMAIN_LOOP_DEPLOYED_AND_VERIFIED",
|
|
controller: "ICE-P-ZY001",
|
|
human_authority: "ICE-GL∞",
|
|
repositories: [
|
|
{name:"bingshuo/chenglu-agent", before:$before_chenglu, after:$after_chenglu},
|
|
{name:"bingshuo/guideng", before:$before_guideng, after:$after_guideng},
|
|
{name:"bingshuo/kezhou", before:$before_kezhou, after:$after_kezhou}
|
|
],
|
|
old_periodic_writers: "DISABLED_AND_INACTIVE",
|
|
old_repository_service: "DISABLED_AND_INACTIVE",
|
|
daily_timer: "ACTIVE",
|
|
role_handshake_services: "ALL_ACTIVE",
|
|
current_code_channel: "HEALTHY",
|
|
daily_dispatch_receipt: $daily_receipt,
|
|
legacy_data_deleted: false,
|
|
source_archive_sha256: $source_archive_sha,
|
|
rule: "Each daily wake reads exact REPO-012 main deltas and writes a signed role-specific update only when Fifth Domain advances."
|
|
}' >"${receipt}.tmp"
|
|
chmod 0600 "${receipt}.tmp"
|
|
mv "${receipt}.tmp" "$receipt"
|
|
|
|
completed=1
|
|
rm -rf "$temporary"
|
|
trap - EXIT
|
|
printf 'THREE_ROLE_PERSONA_SYSTEMS_DAILY_FIFTH_DOMAIN_LOOP_DEPLOYED receipt=%s\n' \
|
|
"$receipt"
|