xworkmate-bridge/scripts/github-actions/deploy-native-binary.sh
2026-06-01 10:02:13 +08:00

91 lines
2.8 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
TARGET_HOST="${1:?target host is required}"
BINARY_PATH="${2:?binary path is required}"
EXPECTED_COMMIT="${3:?expected short commit is required}"
REMOTE_TMP="/tmp/xworkmate-bridge-${EXPECTED_COMMIT}"
REMOTE_BINARY="${REMOTE_BINARY:-/usr/local/bin/xworkmate-go-core}"
STALE_DROPIN="/etc/systemd/system/xworkmate-bridge.service.d/10-hotfix-openclaw-artifacts.conf"
SERVICE_NAME="xworkmate-bridge.service"
if [[ ! "${TARGET_HOST}" =~ ^[A-Za-z0-9._-]+$ ]]; then
echo "invalid target host: ${TARGET_HOST}" >&2
exit 1
fi
if [[ ! "${EXPECTED_COMMIT}" =~ ^[0-9a-f]{7,40}$ ]]; then
echo "invalid expected commit: ${EXPECTED_COMMIT}" >&2
exit 1
fi
if [[ ! -f "${BINARY_PATH}" ]]; then
echo "native bridge binary not found: ${BINARY_PATH}" >&2
exit 1
fi
chmod +x "${BINARY_PATH}"
scp -q "${BINARY_PATH}" "root@${TARGET_HOST}:${REMOTE_TMP}"
ssh "root@${TARGET_HOST}" "EXPECTED_COMMIT='${EXPECTED_COMMIT}' REMOTE_TMP='${REMOTE_TMP}' REMOTE_BINARY='${REMOTE_BINARY}' STALE_DROPIN='${STALE_DROPIN}' SERVICE_NAME='${SERVICE_NAME}' bash -s" <<'REMOTE'
set -euo pipefail
had_immutable=0
restore_immutable() {
if [[ "${had_immutable}" == "1" ]] && command -v chattr >/dev/null 2>&1 && [[ -e "${REMOTE_BINARY}" ]]; then
chattr +i "${REMOTE_BINARY}" 2>/dev/null || true
fi
}
trap restore_immutable EXIT
if command -v lsattr >/dev/null 2>&1 && [[ -e "${REMOTE_BINARY}" ]]; then
attrs="$(lsattr "${REMOTE_BINARY}" 2>/dev/null || true)"
if [[ "${attrs}" == *i* ]]; then
had_immutable=1
chattr -i "${REMOTE_BINARY}"
fi
fi
install -o root -g root -m 0755 "${REMOTE_TMP}" "${REMOTE_BINARY}"
restore_immutable
rm -f "${STALE_DROPIN}"
rmdir --ignore-fail-on-non-empty "$(dirname "${STALE_DROPIN}")" 2>/dev/null || true
version_json="$("${REMOTE_BINARY}" version)"
actual_commit="$(VERSION_JSON="${version_json}" python3 - <<'PY'
import json
import os
payload = json.loads(os.environ["VERSION_JSON"])
print(str(payload.get("commit", "")))
PY
)"
if [[ "${actual_commit}" != "${EXPECTED_COMMIT}" ]]; then
echo "deployed binary commit mismatch: expected ${EXPECTED_COMMIT}, got ${actual_commit}" >&2
exit 1
fi
systemctl daemon-reload
systemctl restart "${SERVICE_NAME}"
deadline=$((SECONDS + 20))
actual_exe=""
pid=""
while (( SECONDS < deadline )); do
pid="$(systemctl show -p MainPID --value "${SERVICE_NAME}")"
if [[ -n "${pid}" && "${pid}" != "0" && -e "/proc/${pid}/exe" ]]; then
actual_exe="$(readlink -f "/proc/${pid}/exe" 2>/dev/null || true)"
if [[ "${actual_exe}" == "${REMOTE_BINARY}" ]]; then
exit 0
fi
fi
sleep 1
done
if [[ -z "${pid}" || "${pid}" == "0" ]]; then
echo "${SERVICE_NAME} did not start" >&2
exit 1
fi
echo "${SERVICE_NAME} is not running ${REMOTE_BINARY}; pid=${pid}; actual=${actual_exe:-unknown}" >&2
exit 1
REMOTE