feat(deploy): add CRM-only online deployment to remote43
- 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: 干净
This commit is contained in:
4
artifacts/online-deploy-20260602/01-local-build.txt
Normal file
4
artifacts/online-deploy-20260602/01-local-build.txt
Normal file
@@ -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
|
||||
21
artifacts/online-deploy-20260602/02-remote-inspect.txt
Normal file
21
artifacts/online-deploy-20260602/02-remote-inspect.txt
Normal file
@@ -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 ===
|
||||
44
artifacts/online-deploy-20260602/03-crm-api-checks.txt
Normal file
44
artifacts/online-deploy-20260602/03-crm-api-checks.txt
Normal file
@@ -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
|
||||
6
artifacts/online-deploy-20260602/04-portal-public.txt
Normal file
6
artifacts/online-deploy-20260602/04-portal-public.txt
Normal file
@@ -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
|
||||
4
artifacts/online-deploy-20260602/05-quality-gates.txt
Normal file
4
artifacts/online-deploy-20260602/05-quality-gates.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
gofmt: (clean)
|
||||
go_vet: (no warnings)
|
||||
go_test_race: PASS
|
||||
integration: PASS
|
||||
46
artifacts/online-deploy-20260602/manifest.json
Normal file
46
artifacts/online-deploy-20260602/manifest.json
Normal file
@@ -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
|
||||
}
|
||||
@@ -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` 作为运行时主键。
|
||||
|
||||
@@ -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/`
|
||||
|
||||
220
scripts/deploy/deploy_crm_only.sh
Normal file
220
scripts/deploy/deploy_crm_only.sh
Normal file
@@ -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" <<EOF
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
exec ssh -N \\
|
||||
-L ${CRM_PORT}:127.0.0.1:${CRM_PORT} \\
|
||||
-i $(printf "%q" "$KEY") \\
|
||||
-o StrictHostKeyChecking=no \\
|
||||
$(printf "%q" "$REMOTE")
|
||||
EOF
|
||||
chmod +x "$LOCAL_TUNNEL_SCRIPT"
|
||||
}
|
||||
|
||||
write_operator_env() {
|
||||
remote43_write_env_file "$LOCAL_OPERATOR_ENV_FILE" \
|
||||
CRM_BASE "http://127.0.0.1:${CRM_PORT}" \
|
||||
REMOTE_CRM_BASE "http://127.0.0.1:${CRM_PORT}" \
|
||||
REMOTE_ROOT "$REMOTE_ROOT" \
|
||||
REMOTE_CRM_ENV_FILE "$REMOTE_CRM_ENV_FILE" \
|
||||
REMOTE_REPO_ROOT "$REMOTE_REPO_ROOT" \
|
||||
KEY "$KEY" \
|
||||
REMOTE "$REMOTE" \
|
||||
crm_admin_token "$crm_admin_token" \
|
||||
crm_admin_username "$crm_admin_username" \
|
||||
crm_admin_password "$crm_admin_password" \
|
||||
CRM_PORT "$CRM_PORT"
|
||||
chmod 600 "$LOCAL_OPERATOR_ENV_FILE"
|
||||
}
|
||||
|
||||
render_crm_only_bootstrap() {
|
||||
local crm_env_q remote_root_q remote_repo_bundle_q
|
||||
local crm_binary_q crm_db_q crm_pid_q crm_log_q remote_repo_root_q crm_port_q
|
||||
printf -v crm_env_q "%q" "$REMOTE_CRM_ENV_FILE"
|
||||
printf -v remote_root_q "%q" "$REMOTE_ROOT"
|
||||
printf -v remote_repo_bundle_q "%q" "$REMOTE_REPO_BUNDLE"
|
||||
printf -v crm_binary_q "%q" "$REMOTE_CRM_BINARY"
|
||||
printf -v crm_db_q "%q" "$REMOTE_CRM_DB_FILE"
|
||||
printf -v crm_pid_q "%q" "$REMOTE_CRM_PID_FILE"
|
||||
printf -v crm_log_q "%q" "$REMOTE_CRM_LOG_FILE"
|
||||
printf -v remote_repo_root_q "%q" "$REMOTE_REPO_ROOT"
|
||||
printf -v crm_port_q "%q" "$CRM_PORT"
|
||||
|
||||
local tmp_bootstrap
|
||||
tmp_bootstrap="$(mktemp)"
|
||||
cat > "$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 <<EOF
|
||||
crm-only stack prepared
|
||||
remote crm base: http://127.0.0.1:${CRM_PORT}
|
||||
remote crm env file: ${REMOTE_CRM_ENV_FILE}
|
||||
remote crm log: ${REMOTE_CRM_LOG_FILE}
|
||||
remote repo root: ${REMOTE_REPO_ROOT}
|
||||
local operator env file: ${LOCAL_OPERATOR_ENV_FILE}
|
||||
local tunnel script: ${LOCAL_TUNNEL_SCRIPT}
|
||||
local deploy dir: ${LOCAL_DEPLOY_DIR}
|
||||
|
||||
next:
|
||||
1. 在另一终端运行: ${LOCAL_TUNNEL_SCRIPT}
|
||||
2. 当前终端执行: set -a; source ${LOCAL_OPERATOR_ENV_FILE}; set +a
|
||||
3. 验证: curl -fsS http://127.0.0.1:${CRM_PORT}/healthz
|
||||
curl -fsS -H "Authorization: Bearer \$crm_admin_token" http://127.0.0.1:${CRM_PORT}/api/packs
|
||||
EOF
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user