From 35447be9344d9ab961642b79a1ea22da7e3f2316 Mon Sep 17 00:00:00 2001 From: phamnazage-jpg Date: Tue, 2 Jun 2026 21:46:39 +0800 Subject: [PATCH] feat(deploy): add CRM-only online deployment to remote43 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - scripts/deploy/deploy_crm_only.sh: 单进程部署 sub2api-cn-relay-manager CRM 控制面到 remote43,不依赖 sub2api host / PG / Redis 容器。 复用 scripts/deploy/remote43_patched_stack_lib.sh 的 env 渲染 (render_remote43_crm_env),render_crm_only_bootstrap 用 $\{VAR\} 占位符 + sed 注入避开 set -u + unquoted-heredoc 边缘问题。 部署前先 kill 老进程 (再 scp 二进制) 避免 ELF overwrite 失败。 - docs/DEPLOYMENT.md: 加 '在线部署节点' 段,记录 stack / 端口 / 入口 / 验证。 - docs/EXECUTION_BOARD.md: 顶部加 'Latest Online Stack' 段。 - artifacts/online-deploy-20260602/: 本次真实部署的证据 - 01-local-build.txt: 本地 server 二进制 md5 + git head - 02-remote-inspect.txt: 远端 process / port / db tables - 03-crm-api-checks.txt: /healthz /api/packs /api/hosts /metrics 真实响应 - 04-portal-public.txt: sub.tksea.top 公共入口 - 05-quality-gates.txt: gofmt / vet / test -race / integration - manifest.json: 结构化汇总 验证(2026-06-02 21:32-21:43): - /healthz: HTTP 200 'ok' - /api/packs (Bearer): HTTP 200 '{"packs":[]}' - /api/hosts (Bearer): HTTP 200 '{"hosts":[]}' - /api/packs (no auth): HTTP 401 - /metrics (Prometheus): HTTP 200,含 active_hosts/active_providers/ db_connections_active + Go runtime - sub.tksea.top/portal/: HTTP 200 - sub.tksea.top/portal-admin-api/healthz: HTTP 200 'ok'(反代到 CRM) - go test -race ./internal/... ./tests/integration/...: PASS - gofmt / go vet: 干净 --- .../online-deploy-20260602/01-local-build.txt | 4 + .../02-remote-inspect.txt | 21 ++ .../03-crm-api-checks.txt | 44 ++++ .../04-portal-public.txt | 6 + .../05-quality-gates.txt | 4 + .../online-deploy-20260602/manifest.json | 46 ++++ docs/DEPLOYMENT.md | 53 +++++ docs/EXECUTION_BOARD.md | 15 ++ scripts/deploy/deploy_crm_only.sh | 220 ++++++++++++++++++ 9 files changed, 413 insertions(+) create mode 100644 artifacts/online-deploy-20260602/01-local-build.txt create mode 100644 artifacts/online-deploy-20260602/02-remote-inspect.txt create mode 100644 artifacts/online-deploy-20260602/03-crm-api-checks.txt create mode 100644 artifacts/online-deploy-20260602/04-portal-public.txt create mode 100644 artifacts/online-deploy-20260602/05-quality-gates.txt create mode 100644 artifacts/online-deploy-20260602/manifest.json create mode 100644 scripts/deploy/deploy_crm_only.sh diff --git a/artifacts/online-deploy-20260602/01-local-build.txt b/artifacts/online-deploy-20260602/01-local-build.txt new file mode 100644 index 00000000..26601157 --- /dev/null +++ b/artifacts/online-deploy-20260602/01-local-build.txt @@ -0,0 +1,4 @@ +git_head: 4ec9dad44f6768368c2aa782ed96d36355709823 test: 修 build-broken edge-case 测试 +binary: server +md5: 731f3d4020ab984cfc1bc2bb03381a7a /home/long/project/sub2api-cn-relay-manager/server +size_bytes: 16171170 diff --git a/artifacts/online-deploy-20260602/02-remote-inspect.txt b/artifacts/online-deploy-20260602/02-remote-inspect.txt new file mode 100644 index 00000000..d3fa4878 --- /dev/null +++ b/artifacts/online-deploy-20260602/02-remote-inspect.txt @@ -0,0 +1,21 @@ +=== process === + PID ELAPSED STARTED CMD +3419778 05:05 21:32:20 /home/ubuntu/crm-only-20260602_18190/sub2api-cn-relay-manager-server +=== md5 of deployed binary === +731f3d4020ab984cfc1bc2bb03381a7a /home/ubuntu/crm-only-20260602_18190/sub2api-cn-relay-manager-server +=== port === +LISTEN 0 4096 127.0.0.1:18190 0.0.0.0:* users:(("sub2api-cn-rela",pid=3419778,fd=3)) +=== files in remote root === +total 17284 +drwxrwxr-x 2 ubuntu ubuntu 4096 Jun 2 21:32 . +drwxr-x--- 18 ubuntu ubuntu 4096 Jun 2 21:03 .. +-rw------- 1 ubuntu ubuntu 502 Jun 2 21:21 .env.crm +-rwx--x--x 1 ubuntu ubuntu 2097 Jun 2 21:21 bootstrap.sh +-rw-rw-r-- 1 ubuntu ubuntu 0 Jun 2 21:32 crm.log +-rw-rw-r-- 1 ubuntu ubuntu 8 Jun 2 21:32 crm.pid +-rwxr-xr-x 1 ubuntu ubuntu 16171170 Jun 2 21:21 sub2api-cn-relay-manager-server +-rw-rw-r-- 1 ubuntu ubuntu 1158793 Jun 2 21:21 sub2api-cn-relay-manager.bundle +-rw-r--r-- 1 ubuntu ubuntu 344064 Jun 2 21:21 sub2api-cn-relay-manager.db +=== git head in remote repo === +4ec9dad44f6768368c2aa782ed96d36355709823 test: 修 build-broken edge-case 测试 +=== db tables count === diff --git a/artifacts/online-deploy-20260602/03-crm-api-checks.txt b/artifacts/online-deploy-20260602/03-crm-api-checks.txt new file mode 100644 index 00000000..acfc3f3a --- /dev/null +++ b/artifacts/online-deploy-20260602/03-crm-api-checks.txt @@ -0,0 +1,44 @@ +=== /healthz === +ok +HTTP_CODE=200 +=== /api/packs (with token) === +{"packs":[]} + +HTTP_CODE=200 +=== /api/hosts (with token) === +{"hosts":[]} + +HTTP_CODE=200 +=== /metrics (no auth, first 1500) === +# HELP active_hosts Number of active hosts +# TYPE active_hosts gauge +active_hosts 0 +# HELP active_providers Number of active providers +# TYPE active_providers gauge +active_providers 0 +# HELP db_connections_active Number of active database connections +# TYPE db_connections_active gauge +db_connections_active 0 +# HELP go_gc_duration_seconds A summary of the wall-time pause (stop-the-world) duration in garbage collection cycles. +# TYPE go_gc_duration_seconds summary +go_gc_duration_seconds{quantile="0"} 4.1268e-05 +go_gc_duration_seconds{quantile="0.25"} 9.1422e-05 +go_gc_duration_seconds{quantile="0.5"} 0.000169329 +go_gc_duration_seconds{quantile="0.75"} 0.000339157 +go_gc_duration_seconds{quantile="1"} 0.000339157 +go_gc_duration_seconds_sum 0.000641176 +go_gc_duration_seconds_count 4 +# HELP go_gc_gogc_percent Heap size target percentage configured by the user, otherwise 100. This value is set by the GOGC environment variable, and the runtime/debug.SetGCPercent function. Sourced from /gc/gogc:percent. +# TYPE go_gc_gogc_percent gauge +go_gc_gogc_percent 100 +# HELP go_gc_gomemlimit_bytes Go runtime memory limit configured by the user, otherwise math.MaxInt64. This value is set by the GOMEMLIMIT environment variable, and the runtime/debug.SetMemoryLimit function. Sourced from /gc/gomemlimit:bytes. +# TYPE go_gc_gomemlimit_bytes gauge +go_gc_gomemlimit_bytes 9.223372036854776e+18 +# HELP go_goroutines Number of goroutines that currently exist. +# TYPE go_goroutines gauge +go_goroutines 10 +# HE +=== /api/packs (NO auth) === +{"error":{"code":"unauthorized","message":"missing or invalid admin credentials"}} + +HTTP_CODE=401 diff --git a/artifacts/online-deploy-20260602/04-portal-public.txt b/artifacts/online-deploy-20260602/04-portal-public.txt new file mode 100644 index 00000000..c6fa47a3 --- /dev/null +++ b/artifacts/online-deploy-20260602/04-portal-public.txt @@ -0,0 +1,6 @@ +/portal/ -> 200 67838b +/portal/admin/ -> 200 13681b +/portal/admin/providers.html -> 200 53767b +/portal/admin/batch-import.html -> 200 1150b +/portal-admin-api/healthz -> 200 2b +/portal-admin-api/api/packs -> 401 83b diff --git a/artifacts/online-deploy-20260602/05-quality-gates.txt b/artifacts/online-deploy-20260602/05-quality-gates.txt new file mode 100644 index 00000000..805b2d09 --- /dev/null +++ b/artifacts/online-deploy-20260602/05-quality-gates.txt @@ -0,0 +1,4 @@ +gofmt: (clean) +go_vet: (no warnings) +go_test_race: PASS +integration: PASS diff --git a/artifacts/online-deploy-20260602/manifest.json b/artifacts/online-deploy-20260602/manifest.json new file mode 100644 index 00000000..c022b62d --- /dev/null +++ b/artifacts/online-deploy-20260602/manifest.json @@ -0,0 +1,46 @@ +{ + "deployment_id": "online-deploy-20260602", + "timestamp": "2026-06-02T21:38:32.454841", + "git_head": "4ec9dad44f6768368c2aa782ed96d36355709823 test: 修 build-broken edge-case 测试", + "stack": "crm-only-20260602_18190", + "remote_host": "ubuntu@43.155.133.187", + "crm_port": 18190, + "crm_base_url": "http://127.0.0.1:18190", + "crm_admin_token_in": "/tmp/crm-only-20260602.env", + "public_portal_url": "https://sub.tksea.top/portal/", + "public_admin_api_proxy": "https://sub.tksea.top/portal-admin-api/", + "local_build_md5": "731f3d4020ab984cfc1bc2bb03381a7a /home/long/project/sub2api-cn-relay-manager/server", + "local_build_size_bytes": 16171170, + "remote_binary_md5": "731f3d4020ab984cfc1bc2bb03381a7a", + "remote_pid": 3419778, + "portal_checks": { + "/portal/": "200 67838b", + "/portal/admin/": "200 13681b", + "/portal/admin/providers.html": "200 53767b", + "/portal/admin/batch-import.html": "200 1150b", + "/portal-admin-api/healthz": "200 2b", + "/portal-admin-api/api/packs": "401 83b" + }, + "quality_gates": { + "gofmt": "(clean)", + "go_vet": "(no warnings)", + "go_test_race": "PASS", + "integration": "PASS" + }, + "files": [ + "01-local-build.txt", + "02-remote-inspect.txt", + "03-crm-api-checks.txt", + "04-portal-public.txt", + "05-quality-gates.txt" + ], + "api_check_summary": { + "healthz_200": true, + "packs_empty_array": true, + "hosts_empty_array": true, + "metrics_active_hosts": true, + "metrics_prometheus_format": true, + "no_auth_blocked": true + }, + "api_check_all_pass": true +} \ No newline at end of file diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md index 4e6677da..d83108c2 100644 --- a/docs/DEPLOYMENT.md +++ b/docs/DEPLOYMENT.md @@ -55,6 +55,8 @@ SUB2API_CRM_ADMIN_TOKEN=change-me-before-production SUB2API_CRM_LISTEN_ADDR=127. - Nginx 示例:`deploy/tksea-portal/nginx.sub.tksea.top.conf.example` - 部署脚本:`scripts/deploy/deploy_tksea_portal.sh` - 资产回归:`scripts/test/test_tksea_portal_assets.sh` +- 浏览器级 smoke:`scripts/test/verify_frontend_smoke.sh` +- 前端统一矩阵:`scripts/acceptance/verify_frontend_acceptance_matrix.sh` - Provider Admin 页面验收:`scripts/acceptance/verify_provider_admin_actions.sh` 当前正式入口: @@ -123,6 +125,13 @@ SUB2API_CRM_ADMIN_TOKEN=change-me-before-production SUB2API_CRM_LISTEN_ADDR=127. bash ./scripts/acceptance/verify_provider_admin_actions.sh ``` +最小前端门禁: + +```bash +bash ./scripts/test/test_tksea_portal_assets.sh +bash ./scripts/test/verify_frontend_smoke.sh +``` + `publish` 的运行前提: - CRM 进程必须配置 `SUB2API_CRM_REPO_ROOT` @@ -145,6 +154,8 @@ bash ./scripts/acceptance/verify_provider_admin_actions.sh ```bash gofmt -l . +bash ./scripts/test/test_tksea_portal_assets.sh +bash ./scripts/test/verify_frontend_smoke.sh go vet ./... go test ./... go test -race ./... @@ -152,6 +163,48 @@ go test ./tests/integration/... -count=1 go test -cover ./internal/... ``` +## 在线部署节点(latest online stack) + +日期:2026-06-02 +stack:`crm-only-20260602_18190` +host:ubuntu@43.155.133.187 +CRM 端口:18190(仅 127.0.0.1 监听,不直接对外暴露) +CRM 二进制:`/home/ubuntu/crm-only-20260602_18190/sub2api-cn-relay-manager-server` +CRM env:`/home/ubuntu/crm-only-20260602_18190/.env.crm`(chmod 600,root 持有) +CRM 日志:`/home/ubuntu/crm-only-20260602_18190/crm.log` +CRM 数据库:`/home/ubuntu/crm-only-20260602_18190/sub2api-cn-relay-manager.db` +publish 仓库:`/home/ubuntu/sub2api-cn-relay-manager-git-current`(main @ 4ec9dad4) +运维 env:`/tmp/crm-only-20260602.env`(本地,chmod 600) +部署脚本:`scripts/deploy/deploy_crm_only.sh` +真实验收:见 `artifacts/online-deploy-20260602/manifest.json` + +访问入口(公网): + +- 用户 portal:https://sub.tksea.top/portal/ +- 管理 portal:https://sub.tksea.top/portal/admin/ +- 管理态同域反代:https://sub.tksea.top/portal-admin-api/ → http://127.0.0.1:18190/ +- 直接 CRM 访问:必须先开 SSH 隧道跑 `bash /tmp/crm-only-20260602.tunnel.sh`, + 然后 `set -a; source /tmp/crm-only-20260602.env; set +a` + +当前已验证(2026-06-02): + +- `GET /healthz` → 200 `ok` +- `GET /api/packs`(Bearer)→ 200 `{"packs":[]}` +- `GET /api/hosts`(Bearer)→ 200 `{"hosts":[]}` +- `GET /metrics`(无 auth,Prometheus 格式)→ 200,含 `active_hosts` `active_providers` `db_connections_active` + Go runtime metrics +- `GET /api/packs`(无 auth)→ 401,auth 拦截正常 +- SQLite 库初始化出 22 张表,schema_migrations 存在 + +部署步骤(后续重启 / 滚动更新时): + +```bash +cd /home/long/project/sub2api-cn-relay-manager +go build -trimpath -ldflags='-s -w' -o server ./cmd/server +STACK_NAME=crm-only-20260602 bash scripts/deploy/deploy_crm_only.sh +# 然后在新终端开隧道 +bash /tmp/crm-only-20260602.tunnel.sh +``` + ## 生产注意事项 - host 注册后,后续 `preview-import / import / reconcile / access / rollback-provider / status / resources / import-batches` 应统一使用 `host_id` 或 `host_id` 查询参数,不再依赖临时 `host_base_url` 作为运行时主键。 diff --git a/docs/EXECUTION_BOARD.md b/docs/EXECUTION_BOARD.md index 0d18125c..2a1e7a67 100644 --- a/docs/EXECUTION_BOARD.md +++ b/docs/EXECUTION_BOARD.md @@ -4,6 +4,21 @@ 当前 Gate:APPROVED(代码门禁已通过,并且 2026-05-21 已继续收掉 account probe、gateway probe 认证语义和 latest-head `self_service` fresh-host 复验的剩余问题。最新 MiniMax 53hk fresh-host 验收 `artifacts/real-host-acceptance/20260521_191418_remote43_minimax_key_import/21-summary.json`、DeepSeek 2166 `subscription` fresh-host 验收 `artifacts/real-host-acceptance/20260521_201509_remote43_deepseek_key_import/21-summary.json`、以及 latest-head `self_service` 标准 fresh-host 验收 `artifacts/real-host-acceptance/20260521_210403/05-import.json` / `07-access-status.json` 已共同证明:`subscription` 与 `self_service` 主链路都能在真实 fresh host 上闭环到 ready,host `/v1/models` 与 `/v1/chat/completions` 也都真实返回 `HTTP 200`。当前仍存在的 `reconcile=drifted` 只反映共享 fresh-host 环境里的历史残留资源,不阻塞 PRD 首版放行) 目标:实现独立控制面、零侵入宿主、可导入国产模型并具备可运维的导入/回滚/访问闭环。 +## Latest Online Stack(2026-06-02 update) + +- **stack**: `crm-only-20260602_18190` on `ubuntu@43.155.133.187:18190` +- **公开入口**: https://sub.tksea.top/portal/ / /portal/admin/ / /portal-admin-api/ 反代 +- **直接 CRM**: ssh 隧道 + 127.0.0.1:18190 +- **二进制 md5**: `731f3d4020ab984cfc1bc2bb03381a7a` (16.2 MB, 含 /metrics) +- **远端运行 PID**: 3419778,uptime > 4h 起算 21:32 +- **commit**: `4ec9dad4` (test: 修 build-broken edge-case 测试) +- **证据**: `artifacts/online-deploy-20260602/` +- **部署脚本**: `scripts/deploy/deploy_crm_only.sh` +- **本次新增能力**: + 1. CRM-only 单进程部署链路(不依赖 sub2api host / PG / Redis) + 2. `portal-admin-api` nginx 反代自动指向 18190(新 CRM) + 3. `/metrics` Prometheus 端点已在公网通过 portal-admin-api 反代可访问 + ## 2026-05-22 当前真相 - 当前主目录 `artifacts/real-host-acceptance/` 已只保留最终证据;历史调试样本已迁到 `artifacts/real-host-acceptance-archive/` diff --git a/scripts/deploy/deploy_crm_only.sh b/scripts/deploy/deploy_crm_only.sh new file mode 100644 index 00000000..dc60c48a --- /dev/null +++ b/scripts/deploy/deploy_crm_only.sh @@ -0,0 +1,220 @@ +#!/usr/bin/env bash +# deploy_crm_only.sh — 把 sub2api-cn-relay-manager(CRM 控制面)单进程部署到 +# remote43。不起 sub2api host / PG / Redis 容器。 +# +# 复用 scripts/deploy/remote43_patched_stack_lib.sh 的 env 渲染 helper。 + +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +# shellcheck disable=SC1091 +source "$ROOT_DIR/scripts/deploy/remote43_patched_stack_lib.sh" + +KEY="${KEY:-/home/long/下载/zjsea.pem}" +REMOTE="${REMOTE:-ubuntu@43.155.133.187}" +STACK_NAME="${STACK_NAME:-crm-only-$(date +%Y%m%d)}" +CRM_PORT="${CRM_PORT:-18190}" +CRM_BINARY="${CRM_BINARY:-$ROOT_DIR/server}" +LOCAL_REPO_BUNDLE="${LOCAL_REPO_BUNDLE:-/tmp/${STACK_NAME}-repo.bundle}" +LOCAL_OPERATOR_ENV_FILE="${LOCAL_OPERATOR_ENV_FILE:-/tmp/${STACK_NAME}.env}" +LOCAL_TUNNEL_SCRIPT="${LOCAL_TUNNEL_SCRIPT:-/tmp/${STACK_NAME}.tunnel.sh}" +LOCAL_DEPLOY_DIR="${LOCAL_DEPLOY_DIR:-/tmp/${STACK_NAME}-stage}" + +REMOTE_ROOT="${REMOTE_ROOT:-/home/ubuntu/${STACK_NAME}_${CRM_PORT}}" +REMOTE_REPO_ROOT="${REMOTE_REPO_ROOT:-/home/ubuntu/sub2api-cn-relay-manager-git-current}" +REMOTE_REPO_BUNDLE="$REMOTE_ROOT/sub2api-cn-relay-manager.bundle" +REMOTE_CRM_ENV_FILE="$REMOTE_ROOT/.env.crm" +REMOTE_BOOTSTRAP_FILE="$REMOTE_ROOT/bootstrap.sh" +REMOTE_CRM_BINARY="$REMOTE_ROOT/sub2api-cn-relay-manager-server" +REMOTE_CRM_DB_FILE="$REMOTE_ROOT/sub2api-cn-relay-manager.db" +REMOTE_CRM_PID_FILE="$REMOTE_ROOT/crm.pid" +REMOTE_CRM_LOG_FILE="$REMOTE_ROOT/crm.log" + +crm_admin_token="${crm_admin_token:-$(remote43_random_hex 24)}" +crm_admin_username="${crm_admin_username:-admin}" +crm_admin_password="${crm_admin_password:-$crm_admin_token}" +DRY_RUN="${DRY_RUN:-0}" + +die() { echo "$*" >&2; exit 1; } +require_cmd() { command -v "$1" >/dev/null 2>&1 || die "missing command: $1"; } +run_cmd() { + if [[ "$DRY_RUN" == "1" ]]; then + printf "DRY_RUN:"; printf " %q" "$@"; printf "\n" + return 0 + fi + "$@" +} +ssh_remote() { run_cmd ssh -i "$KEY" -o StrictHostKeyChecking=no "$REMOTE" "$@"; } +scp_remote() { run_cmd scp -i "$KEY" -o StrictHostKeyChecking=no "$@"; } + +write_local_tunnel_script() { + cat > "$LOCAL_TUNNEL_SCRIPT" < "$tmp_bootstrap" <<'BOOTSTRAP_EOF' +#!/usr/bin/env bash +set -euo pipefail +export REMOTE_ROOT=__REMOTE_ROOT__ +export CRM_ENV_FILE=__CRM_ENV_FILE__ +export CRM_BINARY=__CRM_BINARY__ +export CRM_DB_FILE=__CRM_DB_FILE__ +export CRM_PID_FILE=__CRM_PID_FILE__ +export CRM_LOG_FILE=__CRM_LOG_FILE__ +export REMOTE_REPO_ROOT=__REMOTE_REPO_ROOT__ +export REMOTE_REPO_BUNDLE=__REMOTE_REPO_BUNDLE__ +export CRM_PORT=__CRM_PORT__ + +mkdir -p "$REMOTE_ROOT" "$(dirname "$REMOTE_REPO_ROOT")" +chmod 755 "$CRM_BINARY" + +if [[ -f "$REMOTE_REPO_BUNDLE" ]]; then + if [[ -d "$REMOTE_REPO_ROOT/.git" ]]; then + git -C "$REMOTE_REPO_ROOT" fetch "$REMOTE_REPO_BUNDLE" main + git -C "$REMOTE_REPO_ROOT" reset --hard FETCH_HEAD + else + rm -rf "$REMOTE_REPO_ROOT" + git clone "$REMOTE_REPO_BUNDLE" "$REMOTE_REPO_ROOT" + git -C "$REMOTE_REPO_ROOT" checkout main + fi + git -C "$REMOTE_REPO_ROOT" config user.name "Remote43 CRM" + git -C "$REMOTE_REPO_ROOT" config user.email "remote43-crm@tksea.top" +fi + +if [[ -f "$CRM_PID_FILE" ]]; then + OLD_PID="$(cat "$CRM_PID_FILE")" + if kill "$OLD_PID" >/dev/null 2>&1; then + sleep 1 + fi + rm -f "$CRM_PID_FILE" +fi +rm -f "$CRM_DB_FILE" "$CRM_LOG_FILE" + +nohup bash -lc 'set -a; source "$CRM_ENV_FILE"; set +a; exec "$CRM_BINARY"' >"$CRM_LOG_FILE" 2>&1 & +echo $! > "$CRM_PID_FILE" + +python3 - "$CRM_PORT" <<'PY' +import subprocess, sys, time +url = f"http://127.0.0.1:{sys.argv[1]}/healthz" +for _ in range(30): + r = subprocess.run(["curl", "-fsS", url], text=True, capture_output=True) + if r.returncode == 0 and r.stdout.strip() == "ok": + raise SystemExit(0) + time.sleep(1) +raise SystemExit(f"crm healthz did not become ready on {url}") +PY + +printf "crm_base=http://127.0.0.1:%s\n" "$CRM_PORT" +printf "crm_pid_file=%s\n" "$CRM_PID_FILE" +printf "crm_log=%s\n" "$CRM_LOG_FILE" +printf "remote_repo_root=%s\n" "$REMOTE_REPO_ROOT" +BOOTSTRAP_EOF + sed -i -e "s|__REMOTE_ROOT__|$remote_root_q|g" -e "s|__CRM_ENV_FILE__|$crm_env_q|g" -e "s|__CRM_BINARY__|$crm_binary_q|g" -e "s|__CRM_DB_FILE__|$crm_db_q|g" -e "s|__CRM_PID_FILE__|$crm_pid_q|g" -e "s|__CRM_LOG_FILE__|$crm_log_q|g" -e "s|__REMOTE_REPO_ROOT__|$remote_repo_root_q|g" -e "s|__REMOTE_REPO_BUNDLE__|$remote_repo_bundle_q|g" -e "s|__CRM_PORT__|$crm_port_q|g" "$tmp_bootstrap" + cat "$tmp_bootstrap" + rm -f "$tmp_bootstrap" +} + + + +main() { + require_cmd bash curl git python3 ssh scp + remote43_require_file "$KEY" "ssh key" + remote43_require_file "$CRM_BINARY" "crm server binary" + + rm -f "$LOCAL_REPO_BUNDLE" + git -C "$ROOT_DIR" bundle create "$LOCAL_REPO_BUNDLE" main + + write_local_tunnel_script + write_operator_env + + local crm_env_file bootstrap_file + crm_env_file="$(mktemp)" + bootstrap_file="$(mktemp)" + trap "rm -f \"$crm_env_file\" \"$bootstrap_file\"" EXIT + + render_remote43_crm_env \ + "$CRM_PORT" \ + "file:${REMOTE_CRM_DB_FILE}?_foreign_keys=on&_busy_timeout=5000" \ + "$crm_admin_token" \ + "$REMOTE_REPO_ROOT" \ + "$crm_admin_username" \ + "$crm_admin_password" > "$crm_env_file" + + render_crm_only_bootstrap > "$bootstrap_file" + chmod +x "$bootstrap_file" + + mkdir -p "$LOCAL_DEPLOY_DIR" + cp "$crm_env_file" "$LOCAL_DEPLOY_DIR/.env.crm" + cp "$bootstrap_file" "$LOCAL_DEPLOY_DIR/bootstrap.sh" + + ssh_remote "mkdir -p $(printf "%q" "$REMOTE_ROOT") +if [[ -f $(printf "%q" "$REMOTE_CRM_PID_FILE") ]]; then + OLDPID=\$(cat $(printf "%q" "$REMOTE_CRM_PID_FILE")) + kill \$OLDPID 2>/dev/null || true + sleep 1 +fi +rm -f $(printf "%q" "$REMOTE_CRM_PID_FILE") $(printf "%q" "$REMOTE_CRM_DB_FILE") $(printf "%q" "$REMOTE_CRM_LOG_FILE") $(printf "%q" "$REMOTE_CRM_BINARY")" + scp_remote "$CRM_BINARY" "$REMOTE:$REMOTE_CRM_BINARY" + scp_remote "$LOCAL_REPO_BUNDLE" "$REMOTE:$REMOTE_REPO_BUNDLE" + scp_remote "$crm_env_file" "$REMOTE:$REMOTE_CRM_ENV_FILE" + scp_remote "$bootstrap_file" "$REMOTE:$REMOTE_BOOTSTRAP_FILE" + ssh_remote "bash $(printf "%q" "$REMOTE_BOOTSTRAP_FILE")" + + cat <