deploy(M00-E): 完善菜单式部署骨架

This commit is contained in:
Codex
2026-06-15 16:13:30 +08:00
parent 46c6450ceb
commit c51ba43fa9
18 changed files with 545 additions and 59 deletions
+12 -1
View File
@@ -29,5 +29,16 @@
9. 诊断 9. 诊断
0. 退出 0. 退出
当前版本提供 M00 菜单骨架和安全提示,生产安装动作将在后续 M00-D/M00-E 完善。 当前版本提供 M00-D/M00-E 部署基线:
- 启动快检:架构、基础命令、Docker 禁用提醒、Redis 预留状态。
- 首次安装:创建 `/opt/apps` 目录布局,写入 `run/layout.json`
- 更新业务服务:克隆/检查固定仓库,生成 release manifest。正式后端/后台未生成前仅执行安全 dry-run。
- EMQX:输出原生 Apt/无 Docker/MQTTX 禁止策略和本机命令状态。
- HTTPS:输出固定域名、API 路径和 Nginx 配置检查。
- 状态:显示部署上下文、快检、仓库状态和当前 release manifest。
- 备份:当前生成 manifest-only 备份记录,不触碰真实数据库。
- 恢复/回滚:列出人工输入和回滚点,不自动改动生产数据。
- 诊断:汇总环境、仓库、磁盘和服务状态。
生产执行仍必须由管理员在 Ubuntu 上运行 `/opt/apps/setup.sh` 或同源脚本;Codex 不从 Windows/WSL 自动触发生产部署。
+1 -2
View File
@@ -1,2 +1 @@
0.0.1-m00-baseline 0.1.0-m00-deploy-baseline
+3 -3
View File
@@ -1,13 +1,13 @@
# 部署变更记录 # 部署变更记录
## 2026-06-15 / 0.0.1-m00-baseline ## 2026-06-15 / 0.1.0-m00-deploy-baseline
- 关联模块:M00 - 关联模块:M00
- 关联 commit:本地 HEADpush 待完成) - 关联 commit:本地 HEADpush 待完成)
- 变化内容:建立根目录 `setup.sh`、部署目录和状态文档。 - 变化内容:建立根目录 `setup.sh``scripts/setup/*.sh` 模块化部署脚本、部署目录和状态文档。
- 配置变化:固定 `api.txyundm.cn``/opt/apps`、Ubuntu 24.04 amd64。 - 配置变化:固定 `api.txyundm.cn``/opt/apps`、Ubuntu 24.04 amd64。
- 数据库变化:无。 - 数据库变化:无。
- 兼容性:当前仅菜单骨架,不执行生产改动。 - 兼容性:当前仅菜单骨架,不执行生产改动。
- 已执行验证:待运行脚本 - 已执行验证:Windows 本地检查、WSL `bash -n setup.sh scripts/setup/*.sh``setup.sh --preflight``setup.sh --status``setup.sh --diagnose`;生产 Ubuntu 未执行
- 回滚方式:尚未生产部署,无生产回滚。 - 回滚方式:尚未生产部署,无生产回滚。
- 生产环境人工步骤:后续由管理员在 Ubuntu `/opt/apps/setup.sh` 执行。 - 生产环境人工步骤:后续由管理员在 Ubuntu `/opt/apps/setup.sh` 执行。
+12 -12
View File
@@ -2,7 +2,7 @@
| 项目 | 当前值 | | 项目 | 当前值 |
|---|---| |---|---|
| 菜单脚本版本 | 0.0.1-m00-baseline | | 菜单脚本版本 | 0.1.0-m00-deploy-baseline |
| Gitea 仓库 Web | https://git.txyundm.cn/panda/qipai.git | | Gitea 仓库 Web | https://git.txyundm.cn/panda/qipai.git |
| API 固定域名 | https://api.txyundm.cn | | API 固定域名 | https://api.txyundm.cn |
| 小程序 API | https://api.txyundm.cn/app-api | | 小程序 API | https://api.txyundm.cn/app-api |
@@ -25,18 +25,18 @@
| 命令行 MQTT 工具 | mosquitto-clients:未安装/未验证 | | 命令行 MQTT 工具 | mosquitto-clients:未安装/未验证 |
| Windows MQTTX 验证 | 未验证 | | Windows MQTTX 验证 | 未验证 |
| WSL 环境验证 | 已完成轻量检查、shell 语法检查、临时副本准备和清理;完整构建待正式项目生成 | | WSL 环境验证 | 已完成轻量检查、shell 语法检查、临时副本准备和清理;完整构建待正式项目生成 |
| 最近环境快检 | 2026-06-15 本地骨架检查 | | 最近环境快检 | 2026-06-15 WSL 执行 `setup.sh --preflight/--status/--diagnose` 通过,WARN 项已记录 |
| 最近部署后复检 | 未执行 | | 最近部署后复检 | 未执行 |
| 最近验证 commit | 远端 HEADM00-C 推送脚本语法检查通过 | | 最近验证 commit | 远端 HEADM00-C 推送脚本语法检查通过 |
| 最近验证日期 | 2026-06-15 | | 最近验证日期 | 2026-06-15 |
| 已验证系统 | Ubuntu 24.04 / 未验证 | | 已验证系统 | Ubuntu 24.04 / 未验证 |
| 菜单 1 首次安装 | 未验证 | | 菜单 1 首次安装 | 脚本已实现目录布局;未在生产 Ubuntu 执行 |
| 菜单 2 更新业务 | 未验证 | | 菜单 2 更新业务 | 脚本已实现仓库检查和 dry-run 发布清单;未在生产 Ubuntu 执行 |
| 菜单 3 MQTT | 未验证 | | 菜单 3 MQTT | 状态检查已实现;EMQX 安装未执行 |
| 菜单 4 域名与 HTTPS | 未验证 | | 菜单 4 域名与 HTTPS | 状态检查已实现;证书申请/续期未执行 |
| 菜单 5 状态 | 未验证 | | 菜单 5 状态 | 已实现 |
| 菜单 6 备份 | 未验证 | | 菜单 6 备份 | manifest-only 已实现;真实备份待生产配置 |
| 菜单 7 恢复 | 未验证 | | 菜单 7 恢复 | 人工恢复提示已实现 |
| 菜单 8 回滚 | 未验证 | | 菜单 8 回滚 | 回滚点列表已实现;自动切换待正式 release |
| 菜单 9 诊断 | 未验证 | | 菜单 9 诊断 | 已实现 |
| 已知限制 | 当前仅为 M00 基线骨架,未执行生产部署。 | | 已知限制 | 未执行生产部署WSL 快检显示 Node/Nginx/PM2 缺失或未安装,生产 Ubuntu 需重新验证;正式后端/后台未生成前,业务构建为 dry-run。 |
@@ -11,6 +11,7 @@
- 将含密钥/依赖/真实数据风险的原始参考包和 SQL 从 Git 跟踪中移出,仅保留哈希和审计结论。 - 将含密钥/依赖/真实数据风险的原始参考包和 SQL 从 Git 跟踪中移出,仅保留哈希和审计结论。
- 生成 `docs/reference-page-map.md``docs/db-schema-inventory.md``docs/reference-api-inventory.md` 作为可提交脱敏摘要。 - 生成 `docs/reference-page-map.md``docs/db-schema-inventory.md``docs/reference-api-inventory.md` 作为可提交脱敏摘要。
- 生成 `scripts/dev/windows/push-module.ps1``check-gitea-ssh.ps1`,串联模块检查、提交、推送和远端校验。 - 生成 `scripts/dev/windows/push-module.ps1``check-gitea-ssh.ps1`,串联模块检查、提交、推送和远端校验。
- 生成 `scripts/setup/*.sh` 和新版 `setup.sh` 菜单,覆盖 `/opt/apps` 目录、仓库状态、dry-run 发布清单、备份 manifest、恢复/回滚提示和诊断。
## 起始状态 ## 起始状态
@@ -30,7 +31,7 @@
## 部署影响 ## 部署影响
有。新增 `setup.sh` 菜单骨架和部署状态文档,但未执行生产部署。 有。新增并升级 `setup.sh` `scripts/setup/*.sh` 菜单式部署骨架,但未执行生产部署。
## 测试记录 ## 测试记录
@@ -40,6 +41,7 @@
- 脱敏摘要:已生成页面地图、数据库结构清单和接口线索文档。 - 脱敏摘要:已生成页面地图、数据库结构清单和接口线索文档。
- Windows 推送脚本:已新增,遇到 SSH 主机指纹问题时停止,不自动覆盖 `known_hosts` - Windows 推送脚本:已新增,遇到 SSH 主机指纹问题时停止,不自动覆盖 `known_hosts`
- PowerShell 语法检查:`push-module.ps1``check-gitea-ssh.ps1` 均已通过 Parser 检查。 - PowerShell 语法检查:`push-module.ps1``check-gitea-ssh.ps1` 均已通过 Parser 检查。
- 部署脚本:WSL 已执行 `bash -n setup.sh scripts/setup/*.sh``setup.sh --preflight``setup.sh --status``setup.sh --diagnose`;输出 PASS/WARN,未执行生产写入。
- WSL 检查:已执行 `check-env.sh``check-workspace.sh``verify-linux.sh``prepare-test-copy.sh``cleanup-test-copy.sh`;临时副本创建和清理通过。完整 Linux 构建待正式后端/后台项目生成后执行。 - WSL 检查:已执行 `check-env.sh``check-workspace.sh``verify-linux.sh``prepare-test-copy.sh``cleanup-test-copy.sh`;临时副本创建和清理通过。完整 Linux 构建待正式后端/后台项目生成后执行。
- Git fetch/push:首次多次失败后,`git push origin main` 已成功;随后 `git fetch origin main` 校验 `HEAD == origin/main` 通过。 - Git fetch/push:首次多次失败后,`git push origin main` 已成功;随后 `git fetch origin main` 校验 `HEAD == origin/main` 通过。
+4 -4
View File
@@ -11,9 +11,9 @@
| API-001 | 固定 HTTPS API 域名 | M00-E/M01/M08/M10 | TODO | - | - | DNS/HTTPS 生产验证未执行。 | 在部署脚本和代码中统一 `api.txyundm.cn`。 | | API-001 | 固定 HTTPS API 域名 | M00-E/M01/M08/M10 | TODO | - | - | DNS/HTTPS 生产验证未执行。 | 在部署脚本和代码中统一 `api.txyundm.cn`。 |
| TLS-001 | Nginx 与证书自动化 | M00-E/M10 | TODO | - | - | DNS/80/443 生产验证未执行。 | 补菜单脚本和证书检查。 | | TLS-001 | Nginx 与证书自动化 | M00-E/M10 | TODO | - | - | DNS/80/443 生产验证未执行。 | 补菜单脚本和证书检查。 |
| WXNET-001 | 微信合法域名与真机验证 | M00-E/M08/M10 | TODO | - | - | 微信后台/真机未验证。 | 先完成检查报告模板。 | | WXNET-001 | 微信合法域名与真机验证 | M00-E/M08/M10 | TODO | - | - | 微信后台/真机未验证。 | 先完成检查报告模板。 |
| OPS-001 | 固定 `/opt/apps` 目录 | M00-D/M10 | TODO | - | - | Ubuntu 生产操作未执行。 | 生成部署菜单和目录检查。 | | OPS-001 | 固定 `/opt/apps` 目录 | M00-D/M10 | PARTIAL | 本地 HEAD | `scripts/setup/init-layout.sh` 已生成目录布局和 manifest;未在生产 Ubuntu 执行。 | 生产操作未执行。 | 由管理员在 Ubuntu 菜单执行并记录结果。 |
| OPS-002 | 单仓库 Gitea 推送与拉取部署 | M00-B/M10 | PARTIAL | 远端 HEAD | Windows 到 Gitea `origin/main` 首次 push 与远端校验通过。 | 生产服务器只读拉取部署尚未验证。 | M00-D/M00-E 继续完善生产拉取、菜单部署和状态检查。 | | OPS-002 | 单仓库 Gitea 推送与拉取部署 | M00-B/M10 | PARTIAL | 远端 HEAD | Windows 到 Gitea `origin/main` 首次 push 与远端校验通过。 | 生产服务器只读拉取部署尚未验证。 | M00-D/M00-E 继续完善生产拉取、菜单部署和状态检查。 |
| OPS-003 | 整仓发布清单 | M00-E/M10 | TODO | - | - | - | 生成 release manifest 模板。 | | OPS-003 | 整仓发布清单 | M00-E/M10 | PARTIAL | 本地 HEAD | `deploy-business.sh` 可生成 dry-run release manifest;正式构建待项目生成。 | 后端/后台尚未生成。 | M01/M09 后接入真实构建结果。 |
| OPS-004 | 菜单式更新与环境监测 | M00-E/M10 | TODO | - | - | - | 生成 `setup.sh``scripts/setup`。 | | OPS-004 | 菜单式更新与环境监测 | M00-E/M10 | PARTIAL | 本地 HEAD | `setup.sh` 已接入初始化、更新、MQTT、HTTPS、状态、备份、恢复、回滚和诊断菜单;WSL 快检/status/diagnose 通过。 | 未在生产 Ubuntu 执行。 | 生产执行后补部署记录。 |
| IOT-001 | MQTT Broker 生产部署 | M00/M06 | TODO | - | - | 缺生产 EMQX 实机验证。 | 先完成部署文档和 MQTT 状态模板。 | | IOT-001 | MQTT Broker 生产部署 | M00/M06 | PARTIAL | 本地 HEAD | 菜单 3 已输出 EMQX 原生 Apt、禁止 MQTTX 和 mosquitto-clients 状态检查;未安装。 | 缺生产 EMQX 实机验证。 | M00/M06 继续补官方 Apt 安装和 ACL。 |
| SYS-001 | 微信原生小程序 | M00-C/M08 | TODO | - | - | 参考小程序压缩包尚未深度整理。 | 后续导入正式 `miniapp/`。 | | SYS-001 | 微信原生小程序 | M00-C/M08 | TODO | - | - | 参考小程序压缩包尚未深度整理。 | 后续导入正式 `miniapp/`。 |
+2 -2
View File
@@ -2,11 +2,11 @@
| 项目 | 当前值 | | 项目 | 当前值 |
|---|---| |---|---|
| 最近发布 commit | 未发布;本地 M00 基线提交为本地 HEAD | | 最近发布 commit | 未发布;M00 部署脚本仅生成 dry-run 发布清单 |
| 发布分支 | main | | 发布分支 | main |
| 后端构建 | 未执行 | | 后端构建 | 未执行 |
| 后台构建 | 未执行 | | 后台构建 | 未执行 |
| 小程序检查 | 未执行 | | 小程序检查 | 未执行 |
| 数据库迁移 | 未执行 | | 数据库迁移 | 未执行 |
| 部署结果 | 未部署 | | 部署结果 | 未部署;生产需管理员人工执行菜单 |
| 回滚点 | - | | 回滚点 | - |
+14 -1
View File
@@ -1,4 +1,17 @@
# Ubuntu 菜单脚本目录 # Ubuntu 菜单脚本目录
后续 M00-D/M00-E 将在此目录补充 `/opt/apps` 初始化、Gitea 拉取、业务部署、EMQX、Nginx、证书、备份、恢复、回滚和诊断脚本 本目录为根目录 `setup.sh` 提供模块化函数
| 文件 | 用途 |
|---|---|
| `lib.sh` | 固定路径、仓库、域名、状态输出和架构检查。 |
| `preflight.sh` | 启动快检:架构、基础命令、Docker 禁用提醒、Redis 预留状态。 |
| `init-layout.sh` | 创建 `/opt/apps` 目录布局并写入 `run/layout.json`。 |
| `repo-status.sh` | 检查固定仓库、分支、DIRTY/AHEAD/BEHIND/DIVERGED 状态。 |
| `deploy-business.sh` | 克隆/更新仓库并生成 dry-run release manifest。 |
| `backup.sh` | 生成 manifest-only 备份记录。 |
| `restore.sh` | 输出人工恢复要求,不自动改动生产数据。 |
| `rollback.sh` | 列出 release 回滚点。 |
| `diagnose.sh` | 汇总快检、仓库、磁盘、服务和公开端点。 |
M00 阶段脚本必须保持可重复执行和非破坏性。真实数据库、证书、EMQX ACL、Nginx 写入和 PM2 切换将在后续模块具备配置后继续补全。
+30
View File
@@ -0,0 +1,30 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=lib.sh
. "${SCRIPT_DIR}/lib.sh"
qipai_backup() {
qipai_require_root_for_write
local backup_dir manifest
backup_dir="${APP_ROOT}/backups/manual/$(date +%Y%m%d%H%M%S)"
mkdir -p "$backup_dir"
manifest="${backup_dir}/backup.json"
cat >"$manifest" <<EOF
{
"generatedAt": "$(qipai_timestamp)",
"type": "M00_MANIFEST_ONLY",
"mysql": "SKIPPED_NO_CONFIG",
"emqx": "SKIPPED_NO_CONFIG",
"uploads": "SKIPPED_NO_UPLOAD_DIR",
"note": "Real backup commands will be enabled after production configuration is present."
}
EOF
qipai_pass "backup manifest written: $manifest"
}
if [ "${1:-}" = "--run" ]; then
qipai_backup
fi
+50
View File
@@ -0,0 +1,50 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=lib.sh
. "${SCRIPT_DIR}/lib.sh"
# shellcheck source=repo-status.sh
. "${SCRIPT_DIR}/repo-status.sh"
qipai_deploy_business() {
qipai_require_root_for_write
local repo_dir release_id release_dir manifest
repo_dir="$(qipai_repo_dir)"
if [ ! -d "${repo_dir}/.git" ]; then
qipai_info "cloning repository to ${repo_dir}"
git clone --branch "$QIPAI_BRANCH" "$QIPAI_REPO_URL" "$repo_dir"
fi
qipai_repo_status
git -C "$repo_dir" pull --ff-only origin "$QIPAI_BRANCH"
release_id="$(date +%Y%m%d%H%M%S)-$(git -C "$repo_dir" rev-parse --short HEAD)"
release_dir="$(qipai_release_dir)/${release_id}"
mkdir -p "$release_dir"
manifest="${release_dir}/release.json"
cat >"$manifest" <<EOF
{
"releaseId": "${release_id}",
"generatedAt": "$(qipai_timestamp)",
"commit": "$(git -C "$repo_dir" rev-parse HEAD)",
"branch": "${QIPAI_BRANCH}",
"backendBuild": "SKIPPED_NO_PROJECT",
"adminBuild": "SKIPPED_NO_PROJECT",
"miniappMirror": "RECORDED_SOURCE_COMMIT_ONLY",
"databaseMigration": "SKIPPED_NO_MIGRATIONS",
"deployed": false
}
EOF
ln -sfn "$release_dir" "${APP_ROOT}/current-release"
cp "$manifest" "$(qipai_current_release_file)"
qipai_pass "release manifest written: $manifest"
qipai_warn "business build is a safe M00 dry run until backend/admin projects exist"
}
if [ "${1:-}" = "--run" ]; then
qipai_deploy_business
fi
+33
View File
@@ -0,0 +1,33 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=lib.sh
. "${SCRIPT_DIR}/lib.sh"
# shellcheck source=preflight.sh
. "${SCRIPT_DIR}/preflight.sh"
# shellcheck source=repo-status.sh
. "${SCRIPT_DIR}/repo-status.sh"
qipai_diagnose() {
qipai_preflight || true
qipai_repo_status || true
qipai_info "disk usage:"
df -h "$APP_ROOT" 2>/dev/null || df -h /
qipai_info "service status summary:"
for service in nginx mysql emqx gitea; do
if command -v systemctl >/dev/null 2>&1; then
systemctl is-active --quiet "$service" 2>/dev/null && qipai_pass "${service}: active" || qipai_warn "${service}: inactive or missing"
fi
done
qipai_info "public endpoints:"
qipai_info "app health: ${QIPAI_API_ORIGIN}/app-api/health"
qipai_info "admin health: ${QIPAI_API_ORIGIN}/admin-api/health"
}
if [ "${1:-}" = "--run" ]; then
qipai_diagnose
fi
+62
View File
@@ -0,0 +1,62 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=lib.sh
. "${SCRIPT_DIR}/lib.sh"
qipai_init_layout() {
qipai_require_root_for_write
local dirs=(
"${APP_ROOT}"
"${APP_ROOT}/qipai-repo"
"${APP_ROOT}/qipai-backend"
"${APP_ROOT}/qipai-admin"
"${APP_ROOT}/qipai-miniapp"
"${APP_ROOT}/releases"
"${APP_ROOT}/backups/mysql"
"${APP_ROOT}/backups/emqx"
"${APP_ROOT}/backups/files"
"${APP_ROOT}/logs"
"${APP_ROOT}/nginx"
"${APP_ROOT}/redis-reserved"
"${APP_ROOT}/run"
)
for dir in "${dirs[@]}"; do
mkdir -p "$dir"
qipai_pass "directory exists: $dir"
done
if id qipai >/dev/null 2>&1; then
qipai_pass "user qipai exists"
else
qipai_warn "user qipai does not exist; create manually before production deployment"
fi
if id git >/dev/null 2>&1; then
qipai_pass "user git exists"
else
qipai_warn "user git does not exist; required for native Gitea service"
fi
cat >"${APP_ROOT}/run/layout.json" <<EOF
{
"generatedAt": "$(qipai_timestamp)",
"appRoot": "${APP_ROOT}",
"repo": "$(qipai_repo_dir)",
"backend": "${APP_ROOT}/qipai-backend",
"admin": "${APP_ROOT}/qipai-admin",
"miniapp": "${APP_ROOT}/qipai-miniapp",
"releases": "$(qipai_release_dir)",
"redis": "RESERVED/DISABLED"
}
EOF
qipai_pass "layout manifest written: ${APP_ROOT}/run/layout.json"
}
if [ "${1:-}" = "--run" ]; then
qipai_init_layout
fi
+93
View File
@@ -0,0 +1,93 @@
#!/usr/bin/env bash
set -euo pipefail
QIPAI_DEPLOY_VERSION="${QIPAI_DEPLOY_VERSION:-0.1.0-m00-deploy-baseline}"
APP_ROOT="${APP_ROOT:-/opt/apps}"
QIPAI_REPO_URL="${QIPAI_REPO_URL:-ssh://git@127.0.0.1:2222/panda/qipai.git}"
QIPAI_PUBLIC_REPO_URL="${QIPAI_PUBLIC_REPO_URL:-ssh://git@git.txyundm.cn:2222/panda/qipai.git}"
QIPAI_BRANCH="${QIPAI_BRANCH:-main}"
QIPAI_DOMAIN="${QIPAI_DOMAIN:-api.txyundm.cn}"
QIPAI_API_ORIGIN="https://${QIPAI_DOMAIN}"
qipai_timestamp() {
date "+%Y-%m-%d %H:%M:%S"
}
qipai_status() {
local level="$1"
shift
printf '%s: %s\n' "$level" "$*"
}
qipai_info() {
qipai_status "INFO" "$@"
}
qipai_pass() {
qipai_status "PASS" "$@"
}
qipai_warn() {
qipai_status "WARN" "$@"
}
qipai_fail() {
qipai_status "FAIL" "$@"
}
qipai_require_root_for_write() {
if [ "$(id -u)" -ne 0 ]; then
qipai_fail "This option writes under ${APP_ROOT}; run with sudo on Ubuntu."
return 1
fi
}
qipai_check_arch() {
local kernel_arch dpkg_arch bits
kernel_arch="$(uname -m 2>/dev/null || echo unknown)"
dpkg_arch="$(dpkg --print-architecture 2>/dev/null || echo unknown)"
bits="$(getconf LONG_BIT 2>/dev/null || echo unknown)"
[ "$kernel_arch" = "x86_64" ] && qipai_pass "kernel architecture: ${kernel_arch}" || {
qipai_fail "kernel architecture must be x86_64, actual: ${kernel_arch}"
return 1
}
[ "$dpkg_arch" = "amd64" ] && qipai_pass "dpkg architecture: ${dpkg_arch}" || {
qipai_fail "dpkg architecture must be amd64, actual: ${dpkg_arch}"
return 1
}
[ "$bits" = "64" ] && qipai_pass "userspace bits: ${bits}" || {
qipai_fail "userspace must be 64-bit, actual: ${bits}"
return 1
}
}
qipai_command_status() {
local command_name="$1"
if command -v "$command_name" >/dev/null 2>&1; then
qipai_pass "${command_name}: $(command -v "$command_name")"
else
qipai_warn "${command_name}: not installed"
fi
}
qipai_repo_dir() {
printf '%s/qipai-repo\n' "$APP_ROOT"
}
qipai_release_dir() {
printf '%s/releases\n' "$APP_ROOT"
}
qipai_current_release_file() {
printf '%s/current-release.json\n' "$APP_ROOT"
}
qipai_print_context() {
qipai_info "deploy version: ${QIPAI_DEPLOY_VERSION}"
qipai_info "app root: ${APP_ROOT}"
qipai_info "repo: ${QIPAI_REPO_URL}"
qipai_info "branch: ${QIPAI_BRANCH}"
qipai_info "api origin: ${QIPAI_API_ORIGIN}"
}
+37
View File
@@ -0,0 +1,37 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=lib.sh
. "${SCRIPT_DIR}/lib.sh"
qipai_preflight() {
qipai_print_context
qipai_check_arch
qipai_command_status git
qipai_command_status bash
qipai_command_status node
qipai_command_status npm
qipai_command_status mysql
qipai_command_status nginx
qipai_command_status pm2
qipai_command_status openssl
qipai_command_status curl
if command -v docker >/dev/null 2>&1; then
qipai_warn "docker detected but this project does not use Docker"
else
qipai_pass "docker: not installed or not in PATH"
fi
if command -v redis-server >/dev/null 2>&1; then
qipai_warn "redis-server detected; Redis is reserved/disabled for MVP"
else
qipai_pass "redis: RESERVED/DISABLED"
fi
}
if [ "${1:-}" = "--run" ]; then
qipai_preflight
fi
+52
View File
@@ -0,0 +1,52 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=lib.sh
. "${SCRIPT_DIR}/lib.sh"
qipai_repo_status() {
local repo_dir
repo_dir="$(qipai_repo_dir)"
if [ ! -d "${repo_dir}/.git" ]; then
qipai_warn "repo not cloned: ${repo_dir}"
qipai_info "expected repo: ${QIPAI_REPO_URL}"
return 0
fi
git -C "$repo_dir" remote -v
local branch status
branch="$(git -C "$repo_dir" branch --show-current)"
status="$(git -C "$repo_dir" status --short --branch --untracked-files=all)"
qipai_info "branch: ${branch}"
printf '%s\n' "$status"
if [ "$branch" != "$QIPAI_BRANCH" ]; then
qipai_fail "branch mismatch: expected ${QIPAI_BRANCH}, actual ${branch}"
return 1
fi
if git -C "$repo_dir" diff --quiet && git -C "$repo_dir" diff --cached --quiet; then
qipai_pass "repo is not dirty"
else
qipai_fail "repo is DIRTY; deployment must stop"
return 1
fi
git -C "$repo_dir" fetch origin "$QIPAI_BRANCH"
local counts
counts="$(git -C "$repo_dir" rev-list --left-right --count "${QIPAI_BRANCH}...origin/${QIPAI_BRANCH}")"
qipai_info "ahead/behind: ${counts}"
case "$counts" in
"0 0") qipai_pass "repo matches origin/${QIPAI_BRANCH}" ;;
0$'\t'*) qipai_warn "repo is BEHIND origin/${QIPAI_BRANCH}" ;;
*$'\t'0) qipai_fail "repo is AHEAD origin/${QIPAI_BRANCH}; deployment must stop"; return 1 ;;
*) qipai_fail "repo is DIVERGED; deployment must stop"; return 1 ;;
esac
}
if [ "${1:-}" = "--run" ]; then
qipai_repo_status
fi
+17
View File
@@ -0,0 +1,17 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=lib.sh
. "${SCRIPT_DIR}/lib.sh"
qipai_restore() {
qipai_warn "restore is not automatic in M00 baseline"
qipai_info "Required manual inputs: backup path, MySQL dump, uploads archive, EMQX export"
qipai_info "No production data was changed."
}
if [ "${1:-}" = "--run" ]; then
qipai_restore
fi
+23
View File
@@ -0,0 +1,23 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=lib.sh
. "${SCRIPT_DIR}/lib.sh"
qipai_rollback() {
local releases
releases="$(qipai_release_dir)"
if [ ! -d "$releases" ]; then
qipai_warn "no releases directory: $releases"
return 0
fi
qipai_info "available releases:"
find "$releases" -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | sort -r | head -20
qipai_warn "M00 baseline lists rollback points only; automatic switch requires a selected release id"
}
if [ "${1:-}" = "--run" ]; then
qipai_rollback
fi
+96 -32
View File
@@ -1,52 +1,101 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail set -euo pipefail
VERSION="0.0.1-m00-baseline" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
APP_ROOT="/opt/apps" SETUP_DIR="${SCRIPT_DIR}/scripts/setup"
REPO_SSH="ssh://git@127.0.0.1:2222/panda/qipai.git"
PUBLIC_API="https://api.txyundm.cn" # shellcheck source=scripts/setup/lib.sh
. "${SETUP_DIR}/lib.sh"
# shellcheck source=scripts/setup/preflight.sh
. "${SETUP_DIR}/preflight.sh"
# shellcheck source=scripts/setup/init-layout.sh
. "${SETUP_DIR}/init-layout.sh"
# shellcheck source=scripts/setup/repo-status.sh
. "${SETUP_DIR}/repo-status.sh"
# shellcheck source=scripts/setup/deploy-business.sh
. "${SETUP_DIR}/deploy-business.sh"
# shellcheck source=scripts/setup/backup.sh
. "${SETUP_DIR}/backup.sh"
# shellcheck source=scripts/setup/restore.sh
. "${SETUP_DIR}/restore.sh"
# shellcheck source=scripts/setup/rollback.sh
. "${SETUP_DIR}/rollback.sh"
# shellcheck source=scripts/setup/diagnose.sh
. "${SETUP_DIR}/diagnose.sh"
print_header() { print_header() {
echo "自助棋牌室系统部署菜单 ${VERSION}" echo "自助棋牌室系统部署菜单 ${QIPAI_DEPLOY_VERSION}"
echo "生产目录: ${APP_ROOT}" echo "生产目录: ${APP_ROOT}"
echo "固定 API: ${PUBLIC_API}" echo "固定 API: ${QIPAI_API_ORIGIN}"
echo echo
} }
check_arch() { pause_menu() {
local kernel_arch dpkg_arch bits echo
kernel_arch="$(uname -m 2>/dev/null || echo unknown)" printf "按回车返回菜单..."
dpkg_arch="$(dpkg --print-architecture 2>/dev/null || echo unknown)" read -r _ || true
bits="$(getconf LONG_BIT 2>/dev/null || echo unknown)" }
echo "内核架构: ${kernel_arch}"
echo "DPKG 架构: ${dpkg_arch}" run_action() {
echo "用户空间位数: ${bits}" local title="$1"
if [ "${kernel_arch}" != "x86_64" ] || [ "${dpkg_arch}" != "amd64" ] || [ "${bits}" != "64" ]; then shift
echo "FAIL: 仅支持 Ubuntu 24.04 x86-64/amd64。" echo
exit 1 echo "== ${title} =="
"$@"
pause_menu
}
show_status() {
qipai_print_context
qipai_preflight || true
qipai_repo_status || true
if [ -f "$(qipai_current_release_file)" ]; then
qipai_info "current release manifest: $(qipai_current_release_file)"
cat "$(qipai_current_release_file)"
else
qipai_warn "current release manifest not found"
fi fi
} }
quick_check() { show_mqtt_status() {
echo "== 启动快检 ==" qipai_info "EMQX target: native Ubuntu Apt package, no Docker"
check_arch qipai_info "MQTT broker host: 101.42.38.246"
command -v git >/dev/null 2>&1 && echo "Git: PASS" || echo "Git: WARN 未安装" qipai_info "MQTTX on server: forbidden"
command -v nginx >/dev/null 2>&1 && echo "Nginx: PASS" || echo "Nginx: WARN 未安装" if command -v emqx >/dev/null 2>&1; then
command -v node >/dev/null 2>&1 && echo "Node.js: PASS" || echo "Node.js: WARN 未安装" qipai_pass "emqx command exists"
echo "仓库: ${REPO_SSH}" emqx version 2>/dev/null || true
else
qipai_warn "emqx command not found"
fi
if command -v mosquitto_pub >/dev/null 2>&1; then
qipai_pass "mosquitto-clients available"
else
qipai_warn "mosquitto-clients not installed"
fi
} }
not_implemented() { show_https_status() {
echo "当前 M00 基线仅提供菜单骨架;该选项将在后续子阶段完善。" qipai_info "domain: ${QIPAI_DOMAIN}"
qipai_info "origin: ${QIPAI_API_ORIGIN}"
qipai_info "app api: ${QIPAI_API_ORIGIN}/app-api"
qipai_info "admin api: ${QIPAI_API_ORIGIN}/admin-api"
if command -v nginx >/dev/null 2>&1; then
nginx -t
else
qipai_warn "nginx not installed"
fi
if command -v openssl >/dev/null 2>&1; then
qipai_info "TLS live check requires public DNS/network and is not forced in M00"
fi
} }
main_menu() { main_menu() {
while true; do while true; do
print_header print_header
echo "1. 首次安装" echo "1. 首次安装 / 初始化目录"
echo "2. 更新业务服务" echo "2. 更新业务服务 / 生成发布清单"
echo "3. 安装或检查 EMQX" echo "3. 安装或检查 EMQX"
echo "4. 配置域名与 HTTPS" echo "4. 配置域名与 HTTPS / 状态检查"
echo "5. 查看状态" echo "5. 查看状态"
echo "6. 备份" echo "6. 备份"
echo "7. 恢复" echo "7. 恢复"
@@ -56,14 +105,29 @@ main_menu() {
printf "请选择: " printf "请选择: "
read -r choice read -r choice
case "${choice}" in case "${choice}" in
1|2|3|4|6|7|8) not_implemented ;; 1) run_action "首次安装 / 初始化目录" qipai_init_layout ;;
5|9) quick_check ;; 2) run_action "更新业务服务 / 生成发布清单" qipai_deploy_business ;;
3) run_action "安装或检查 EMQX" show_mqtt_status ;;
4) run_action "配置域名与 HTTPS / 状态检查" show_https_status ;;
5) run_action "查看状态" show_status ;;
6) run_action "备份" qipai_backup ;;
7) run_action "恢复" qipai_restore ;;
8) run_action "回滚" qipai_rollback ;;
9) run_action "诊断" qipai_diagnose ;;
0) exit 0 ;; 0) exit 0 ;;
*) echo "无效选项" ;; *) echo "无效选项"; pause_menu ;;
esac esac
echo echo
done done
} }
if [ "${1:-}" = "--preflight" ]; then
qipai_preflight
elif [ "${1:-}" = "--status" ]; then
show_status
elif [ "${1:-}" = "--diagnose" ]; then
qipai_diagnose
else
main_menu "$@" main_menu "$@"
fi