230 lines
7.3 KiB
Shell
Executable file
230 lines
7.3 KiB
Shell
Executable file
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
expected_machine_id="caa7b1019517470f9d1368b6e79db49e"
|
|
state_root="/var/lib/guanghu/personas/guanghu/hlcc-v16.0.1"
|
|
binary="${state_root}/release/forgejo-16.0.1-linux-amd64"
|
|
config="${state_root}/config/app.ini"
|
|
work_path="${state_root}/data"
|
|
database="${state_root}/data/hlcc.db"
|
|
api="http://127.0.0.1:3340/api/v1"
|
|
receipt_root="/var/lib/guanghu/architecture-provision/receipts"
|
|
receipt="${receipt_root}/HLCC-PERSONA-WRITER-RETIREMENT-20260806.json"
|
|
archive_root="/var/lib/guanghu/legacy-archives/persona-writers/20260806"
|
|
token_name="hlcc-persona-final-sync-20260806"
|
|
temporary="$(mktemp -d)"
|
|
credential_file="${temporary}/credentials"
|
|
token=""
|
|
completed=0
|
|
|
|
writers=(
|
|
chenglu-agent.service
|
|
chenglu-daily.timer
|
|
guideng-agent.service
|
|
kezhou-daily.timer
|
|
)
|
|
oneshots=(
|
|
chenglu-daily.service
|
|
kezhou-daily.service
|
|
)
|
|
keepers=(
|
|
chenglu-team-handshake.service
|
|
guideng-team-handshake.service
|
|
kezhou-team-handshake.service
|
|
)
|
|
|
|
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"
|
|
)
|
|
declare -A work_repositories=(
|
|
[chenglu-agent]="/var/lib/chenglu-agent/repository"
|
|
[guideng]="/var/lib/guanghu/personas/guideng/repository"
|
|
[kezhou]="/var/lib/guanghu/personas/kezhou/repository"
|
|
)
|
|
|
|
cleanup_token() {
|
|
if test -n "$token"; then
|
|
TOKEN_NAME="$token_name" DATABASE="$database" python3 - <<'PY'
|
|
import os
|
|
import sqlite3
|
|
|
|
connection = sqlite3.connect(os.environ["DATABASE"], timeout=15)
|
|
try:
|
|
with connection:
|
|
owner = connection.execute(
|
|
"select id from user where lower_name = ?",
|
|
("bingshuo",),
|
|
).fetchone()
|
|
if owner:
|
|
connection.execute(
|
|
"delete from access_token where uid = ? and name = ?",
|
|
(owner[0], os.environ["TOKEN_NAME"]),
|
|
)
|
|
finally:
|
|
connection.close()
|
|
PY
|
|
fi
|
|
rm -f "$credential_file"
|
|
rmdir "$temporary" 2>/dev/null || true
|
|
}
|
|
|
|
rollback() {
|
|
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
|
|
cleanup_token
|
|
}
|
|
|
|
on_exit() {
|
|
status=$?
|
|
trap - EXIT
|
|
if test "$completed" = 0; then
|
|
rollback
|
|
else
|
|
cleanup_token
|
|
fi
|
|
exit "$status"
|
|
}
|
|
trap on_exit EXIT
|
|
|
|
test "$(cat /etc/machine-id)" = "$expected_machine_id"
|
|
test -x "$binary"
|
|
test -f "$config"
|
|
test -f "$database"
|
|
test "$(systemctl is-active hlcc-jd-candidate.service)" = "active"
|
|
test "$(curl -fsS http://127.0.0.1:3341/health | jq -r .ready)" = "true"
|
|
for unit in "${keepers[@]}"; do
|
|
test "$(systemctl is-active "$unit")" = "active"
|
|
done
|
|
for name in chenglu-agent guideng kezhou; do
|
|
test -d "${old_repositories[$name]}"
|
|
test -d "${state_root}/data/repositories/bingshuo/${name}.git"
|
|
git --git-dir="${old_repositories[$name]}" fsck --connectivity-only --no-dangling
|
|
done
|
|
|
|
mkdir -p "$archive_root" "$receipt_root"
|
|
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 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
|
|
|
|
token="$(
|
|
runuser -u guanghu -- "$binary" admin user generate-access-token \
|
|
--username bingshuo \
|
|
--token-name "$token_name" \
|
|
--scopes write:repository,write:user \
|
|
--raw \
|
|
--config "$config" \
|
|
--work-path "$work_path" |
|
|
tail -n 1
|
|
)"
|
|
test -n "$token"
|
|
test "${#token}" -ge 32
|
|
printf 'http://bingshuo:%s@127.0.0.1:3340\n' "$token" >"$credential_file"
|
|
chmod 0600 "$credential_file"
|
|
|
|
for name in chenglu-agent guideng kezhou; do
|
|
source="${old_repositories[$name]}"
|
|
target="http://127.0.0.1:3340/bingshuo/${name}.git"
|
|
git -c "credential.helper=store --file ${credential_file}" \
|
|
--git-dir="$source" push --prune "$target" \
|
|
"refs/heads/*:refs/heads/*" \
|
|
"refs/tags/*:refs/tags/*"
|
|
|
|
new_bare="${state_root}/data/repositories/bingshuo/${name}.git"
|
|
old_refs="$(
|
|
git --git-dir="$source" for-each-ref \
|
|
--format='%(refname) %(objectname)' refs/heads refs/tags |
|
|
sort
|
|
)"
|
|
new_refs="$(
|
|
git --git-dir="$new_bare" for-each-ref \
|
|
--format='%(refname) %(objectname)' refs/heads refs/tags |
|
|
sort
|
|
)"
|
|
test "$new_refs" = "$old_refs"
|
|
runuser -u guanghu -- \
|
|
git -c "safe.directory=$new_bare" --git-dir="$new_bare" \
|
|
fsck --connectivity-only --no-dangling
|
|
|
|
git -C "${work_repositories[$name]}" remote set-url origin \
|
|
"https://guanghulab.com/code/bingshuo/${name}.git"
|
|
done
|
|
|
|
systemctl disable --now guanghu-forgejo.service
|
|
test "$(systemctl is-active guanghu-forgejo.service || true)" != "active"
|
|
for unit in "${keepers[@]}"; do
|
|
test "$(systemctl is-active "$unit")" = "active"
|
|
done
|
|
test "$(curl -fsS http://127.0.0.1:3341/health | jq -r .ready)" = "true"
|
|
if ss -ltnH | awk '{print $4}' | grep -Eq '(^|:)3001$'; then
|
|
exit 41
|
|
fi
|
|
|
|
chenglu_sha="$(git --git-dir="${old_repositories[chenglu-agent]}" rev-parse refs/heads/main)"
|
|
guideng_sha="$(git --git-dir="${old_repositories[guideng]}" rev-parse refs/heads/main)"
|
|
kezhou_sha="$(git --git-dir="${old_repositories[kezhou]}" rev-parse refs/heads/main)"
|
|
|
|
jq -n \
|
|
--arg completed_at "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
|
--arg chenglu "$chenglu_sha" \
|
|
--arg guideng "$guideng_sha" \
|
|
--arg kezhou "$kezhou_sha" \
|
|
--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-writer-retirement-receipt/v1",
|
|
receipt_id: "HLCC-PERSONA-WRITER-RETIREMENT-20260806",
|
|
target_node: "JD-FD-PRIMARY",
|
|
completed_at: $completed_at,
|
|
result: "PERIODIC_WRITERS_RETIRED_AFTER_EXACT_HISTORY_MIGRATION",
|
|
controller: "ICE-P-ZY001",
|
|
human_authority: "ICE-GL∞",
|
|
repositories: [
|
|
{name:"bingshuo/chenglu-agent", main_sha:$chenglu, refs:"EXACT_MATCH"},
|
|
{name:"bingshuo/guideng", main_sha:$guideng, refs:"EXACT_MATCH"},
|
|
{name:"bingshuo/kezhou", main_sha:$kezhou, refs:"EXACT_MATCH"}
|
|
],
|
|
periodic_writers: "DISABLED_AND_INACTIVE",
|
|
old_repository_service: "DISABLED_AND_INACTIVE",
|
|
role_handshake_services: "ALL_ACTIVE",
|
|
current_code_channel: "HEALTHY",
|
|
legacy_data_deleted: false,
|
|
source_archive_sha256: $source_archive_sha,
|
|
rule: "Scheduled repository writes are historical transport, not proof of language or persona continuity."
|
|
}' >"${receipt}.tmp"
|
|
chmod 0600 "${receipt}.tmp"
|
|
mv "${receipt}.tmp" "$receipt"
|
|
|
|
completed=1
|
|
token_to_delete="$token"
|
|
cleanup_token
|
|
token=""
|
|
trap - EXIT
|
|
printf 'PERIODIC_WRITERS_RETIRED receipt=%s token_deleted=%s\n' \
|
|
"$receipt" "$(test -n "$token_to_delete" && printf true)"
|