fix: QA remediation P1-D + P2 for infra/vps orchestrator (v1.8.0)
P1-D: bump package.json version 1.7.2 → 1.8.0 and add [1.8.0] CHANGELOG
entry documenting the infra/vps/ PRAB generation orchestrator addition.
P2 (security): replace StrictHostKeyChecking=no with StrictHostKeyChecking=accept-new
in all three SSH invocations in orchestrate-generation.sh. Add
UserKnownHostsFile=${KNOWN_HOSTS} (-o) to pin key verification to the
pre-seeded file rather than the root known_hosts.
install-orchestrator.sh: add ssh-keyscan step that pre-populates
/opt/prab-orchestrator/known_hosts with Hostinger's host key on first
install. Graceful degradation: if ssh-keyscan fails, logs WARN and
proceeds (accept-new will add the key on first real connect).
prab-generation.service: add justifying comment for User=root (root holds
the Hostinger SSH key + known_hosts in /root/.ssh/ and /opt/prab-orchestrator/).
Agent-Session: prab-reliability-remediation-20260622
This commit is contained in:
17
CHANGELOG.md
17
CHANGELOG.md
@@ -1,5 +1,22 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [1.8.0] - 2026-06-23
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- **`infra/vps/` PRAB generation orchestrator**: systemd timer + service + bash
|
||||||
|
script that SSHes from the Coolify KVM8 VPS to Hostinger and drives
|
||||||
|
`wp prautoblogger generate --sync` in a held-open SSH session. Sidesteps
|
||||||
|
Hostinger's ~10-min background-PHP-process kill that was blocking full
|
||||||
|
generation runs. Components:
|
||||||
|
- `orchestrate-generation.sh`: main script with DRY_RUN mode, structured
|
||||||
|
logging, reaper pre-sweep, failure email alert, and shellcheck-clean quoting.
|
||||||
|
- `prab-generation.service` / `prab-generation.timer`: systemd oneshot service
|
||||||
|
(2h timeout, no auto-restart) + daily 06:00 UTC timer with 5-min jitter.
|
||||||
|
- `install-orchestrator.sh`: one-time setup script; installs units but does NOT
|
||||||
|
enable the timer (requires explicit CEO greenlight post-QA).
|
||||||
|
- Security: `StrictHostKeyChecking=accept-new` with pre-seeded known_hosts for
|
||||||
|
the Hostinger IP; no hardcoded secrets; SSH key referenced by env-var path.
|
||||||
|
|
||||||
## [1.7.2] - 2026-06-17
|
## [1.7.2] - 2026-06-17
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -45,6 +45,21 @@ mkdir -p "${INSTALL_DIR}"
|
|||||||
install -m 0750 "${ORCHESTRATE_SH}" "${INSTALL_DIR}/orchestrate-generation.sh"
|
install -m 0750 "${ORCHESTRATE_SH}" "${INSTALL_DIR}/orchestrate-generation.sh"
|
||||||
echo " OK: ${INSTALL_DIR}/orchestrate-generation.sh"
|
echo " OK: ${INSTALL_DIR}/orchestrate-generation.sh"
|
||||||
|
|
||||||
|
# Pre-seed known_hosts with Hostinger's host key so ssh(1) can verify
|
||||||
|
# the server identity without StrictHostKeyChecking=no.
|
||||||
|
# ssh-keyscan output is pinned here; re-run if Hostinger rotates its key.
|
||||||
|
KNOWN_HOSTS_FILE="${INSTALL_DIR}/known_hosts"
|
||||||
|
if [[ ! -f "${KNOWN_HOSTS_FILE}" ]]; then
|
||||||
|
echo "Pre-seeding known_hosts from Hostinger (${SSH_HOST:-145.223.107.228}:${SSH_PORT:-65002})..."
|
||||||
|
ssh-keyscan -p "${SSH_PORT:-65002}" "${SSH_HOST:-145.223.107.228}" \
|
||||||
|
> "${KNOWN_HOSTS_FILE}" 2>/dev/null \
|
||||||
|
&& chmod 0600 "${KNOWN_HOSTS_FILE}" \
|
||||||
|
&& echo " OK: ${KNOWN_HOSTS_FILE}" \
|
||||||
|
|| echo " WARN: ssh-keyscan failed — known_hosts not seeded; first SSH will add key via accept-new"
|
||||||
|
else
|
||||||
|
echo " OK: ${KNOWN_HOSTS_FILE} (already exists, not overwritten)"
|
||||||
|
fi
|
||||||
|
|
||||||
echo "Installing systemd units..."
|
echo "Installing systemd units..."
|
||||||
install -m 0644 "${SERVICE_FILE}" "${SYSTEMD_DIR}/prab-generation.service"
|
install -m 0644 "${SERVICE_FILE}" "${SYSTEMD_DIR}/prab-generation.service"
|
||||||
install -m 0644 "${TIMER_FILE}" "${SYSTEMD_DIR}/prab-generation.timer"
|
install -m 0644 "${TIMER_FILE}" "${SYSTEMD_DIR}/prab-generation.timer"
|
||||||
|
|||||||
@@ -36,6 +36,9 @@ SSH_HOST="${SSH_HOST:-145.223.107.228}"
|
|||||||
SSH_PORT="${SSH_PORT:-65002}"
|
SSH_PORT="${SSH_PORT:-65002}"
|
||||||
SSH_USER="${SSH_USER:-u117248512}"
|
SSH_USER="${SSH_USER:-u117248512}"
|
||||||
WP_PATH="${WP_PATH:-/home/u117248512/domains/peptiderepo.com/public_html}"
|
WP_PATH="${WP_PATH:-/home/u117248512/domains/peptiderepo.com/public_html}"
|
||||||
|
# Known-hosts file pre-seeded by install-orchestrator.sh with Hostinger's host key.
|
||||||
|
# Pins the server identity; StrictHostKeyChecking=accept-new adds on first connect.
|
||||||
|
KNOWN_HOSTS="${KNOWN_HOSTS:-/opt/prab-orchestrator/known_hosts}"
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Logging helpers
|
# Logging helpers
|
||||||
@@ -66,7 +69,7 @@ fi
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
if [[ "${DRY_RUN}" == "1" ]]; then
|
if [[ "${DRY_RUN}" == "1" ]]; then
|
||||||
log "DRY_RUN=1 — echoing commands only, no generation"
|
log "DRY_RUN=1 — echoing commands only, no generation"
|
||||||
log "WOULD RUN: ssh -i ${SSH_KEY} -p ${SSH_PORT} -o StrictHostKeyChecking=no -o ConnectTimeout=15 ${SSH_USER}@${SSH_HOST} \\"
|
log "WOULD RUN: ssh -i ${SSH_KEY} -p ${SSH_PORT} -o StrictHostKeyChecking=accept-new -o ConnectTimeout=15 ${SSH_USER}@${SSH_HOST} \\"
|
||||||
log " 'wp prautoblogger generate --sync --path=${WP_PATH} 2>&1'"
|
log " 'wp prautoblogger generate --sync --path=${WP_PATH} 2>&1'"
|
||||||
log "DRY_RUN complete"
|
log "DRY_RUN complete"
|
||||||
exit 0
|
exit 0
|
||||||
@@ -90,7 +93,8 @@ REAP_OUT=""
|
|||||||
# shellcheck disable=SC2029
|
# shellcheck disable=SC2029
|
||||||
REAP_OUT="$(ssh -i "${SSH_KEY}" \
|
REAP_OUT="$(ssh -i "${SSH_KEY}" \
|
||||||
-p "${SSH_PORT}" \
|
-p "${SSH_PORT}" \
|
||||||
-o StrictHostKeyChecking=no \
|
-o StrictHostKeyChecking=accept-new \
|
||||||
|
-o UserKnownHostsFile="${KNOWN_HOSTS}" \
|
||||||
-o ConnectTimeout=15 \
|
-o ConnectTimeout=15 \
|
||||||
-o ServerAliveInterval=60 \
|
-o ServerAliveInterval=60 \
|
||||||
-o ServerAliveCountMax=30 \
|
-o ServerAliveCountMax=30 \
|
||||||
@@ -112,7 +116,8 @@ EXIT_CODE=0
|
|||||||
# shellcheck disable=SC2029
|
# shellcheck disable=SC2029
|
||||||
RESULT_OUTPUT="$(ssh -i "${SSH_KEY}" \
|
RESULT_OUTPUT="$(ssh -i "${SSH_KEY}" \
|
||||||
-p "${SSH_PORT}" \
|
-p "${SSH_PORT}" \
|
||||||
-o StrictHostKeyChecking=no \
|
-o StrictHostKeyChecking=accept-new \
|
||||||
|
-o UserKnownHostsFile="${KNOWN_HOSTS}" \
|
||||||
-o ConnectTimeout=15 \
|
-o ConnectTimeout=15 \
|
||||||
-o ServerAliveInterval=60 \
|
-o ServerAliveInterval=60 \
|
||||||
-o ServerAliveCountMax=30 \
|
-o ServerAliveCountMax=30 \
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ After=network.target
|
|||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
|
# root owns /root/.ssh/hostinger_prod_ed25519 and /opt/prab-orchestrator/known_hosts;
|
||||||
|
# a dedicated service user would need its own keypair + sudo-less SSH access.
|
||||||
|
# Acceptable on this dedicated lab VPS (not shared); revisit if VPS is multi-tenant.
|
||||||
User=root
|
User=root
|
||||||
ExecStart=/opt/prab-orchestrator/orchestrate-generation.sh
|
ExecStart=/opt/prab-orchestrator/orchestrate-generation.sh
|
||||||
StandardOutput=journal
|
StandardOutput=journal
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "peptide-e2e",
|
"name": "peptide-e2e",
|
||||||
"version": "1.7.2",
|
"version": "1.8.0",
|
||||||
"description": "Playwright smoke tests for peptiderepo.com (staging + production)",
|
"description": "Playwright smoke tests for peptiderepo.com (staging + production)",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
Reference in New Issue
Block a user