diff --git a/scripts/TestDevlop.py b/scripts/TestDevlop.py index 7ce79ced..a9f1c5c9 100644 --- a/scripts/TestDevlop.py +++ b/scripts/TestDevlop.py @@ -4,8 +4,8 @@ Soul 创业派对 - 统一部署脚本(支持两种模式) 模式一 devlop(默认):dist 切换方式 - 本地 pnpm build → 打包 zip → 上传解压到 dist2 → 宝塔暂停 → dist→dist1, dist2→dist → 重启 - 用法: python scripts/devlop.py [--no-build] + 本地 pnpm build → 打包 zip → 上传解压到 dist2 → 在 dist2 执行 pnpm install 并等待完成 → 再切换目录、重启 + 用法: python scripts/TestDevlop.py [--no-build] 模式二 deploy:直接覆盖方式 本地打包 tar.gz → SSH 上传解压到项目目录 → 宝塔 API 重启 @@ -16,7 +16,8 @@ Soul 创业派对 - 统一部署脚本(支持两种模式) DEPLOY_PROJECT_PATH # deploy 模式项目路径,默认 /www/wwwroot/soulTest DEVOP_BASE_PATH # devlop 模式目录,默认 /www/wwwroot/auto-devlop/soulTest BAOTA_PANEL_URL / BAOTA_API_KEY / DEPLOY_PM2_APP - DEPLOY_PORT / DEPLOY_NODE_VERSION / DEPLOY_NODE_PATH + DEPLOY_PORT # 应用端口,统一由此读取,默认 30066(见 DEFAULT_DEPLOY_PORT) + DEPLOY_NODE_VERSION / DEPLOY_NODE_PATH """ from __future__ import print_function @@ -52,6 +53,12 @@ except ImportError: # ==================== 配置 ==================== +# 端口统一从环境变量 DEPLOY_PORT 读取,未设置时使用此默认值 +DEPLOY_PM2_APP = "testsoul" +DEFAULT_DEPLOY_PORT = 30066 +DEPLOY_PROJECT_PATH = "/www/wwwroot/soulTest" +DEPLOY_SITE_URL = "https://soulTest.quwanzhi.com" + def get_cfg(): """获取基础部署配置(deploy 模式与 devlop 共用 SSH/宝塔)""" return { @@ -59,12 +66,12 @@ def get_cfg(): "user": os.environ.get("DEPLOY_USER", "root"), "password": os.environ.get("DEPLOY_PASSWORD", "Zhiqun1984"), "ssh_key": os.environ.get("DEPLOY_SSH_KEY", ""), - "project_path": os.environ.get("DEPLOY_PROJECT_PATH", "/www/wwwroot/soulTest"), + "project_path": os.environ.get("DEPLOY_PROJECT_PATH", DEPLOY_PROJECT_PATH), "panel_url": os.environ.get("BAOTA_PANEL_URL", "https://42.194.232.22:9988"), "api_key": os.environ.get("BAOTA_API_KEY", "hsAWqFSi0GOCrunhmYdkxy92tBXfqYjd"), - "pm2_name": os.environ.get("DEPLOY_PM2_APP", "soulTest"), - "site_url": os.environ.get("DEPLOY_SITE_URL", "https://soulTest.quwanzhi.com"), - "port": int(os.environ.get("DEPLOY_PORT", "30066")), + "pm2_name": os.environ.get("DEPLOY_PM2_APP", DEPLOY_PM2_APP), + "site_url": os.environ.get("DEPLOY_SITE_URL", DEPLOY_SITE_URL), + "port": int(os.environ.get("DEPLOY_PORT", str(DEFAULT_DEPLOY_PORT))), "node_version": os.environ.get("DEPLOY_NODE_VERSION", "v22.14.0"), "node_path": os.environ.get("DEPLOY_NODE_PATH", "/www/server/nodejs/v22.14.0/bin"), } @@ -152,7 +159,9 @@ def restart_node_project(panel_url, api_key, pm2_name): return False -def add_or_update_node_project(panel_url, api_key, pm2_name, project_path, port=30006, node_path=None): +def add_or_update_node_project(panel_url, api_key, pm2_name, project_path, port=None, node_path=None): + if port is None: + port = int(os.environ.get("DEPLOY_PORT", str(DEFAULT_DEPLOY_PORT))) port_env = "PORT=%d " % port run_cmd = port_env + ("%s/node server.js" % node_path if node_path else "node server.js") payload = {"name": pm2_name, "path": project_path, "run_cmd": run_cmd, "port": str(port)} @@ -402,7 +411,7 @@ def deploy_via_baota_api(cfg): panel_url, api_key, pm2_name = cfg["panel_url"], cfg["api_key"], cfg["pm2_name"] project_path = cfg["project_path"] node_path = cfg.get("node_path", "/www/server/nodejs/v22.14.0/bin") - port = cfg.get("port", 30006) + port = cfg["port"] if not get_node_project_status(panel_url, api_key, pm2_name): add_or_update_node_project(panel_url, api_key, pm2_name, project_path, port, node_path) @@ -482,11 +491,12 @@ def pack_standalone_zip(root): server_js = os.path.join(staging, "server.js") if os.path.isfile(server_js): try: + deploy_port = int(os.environ.get("DEPLOY_PORT", str(DEFAULT_DEPLOY_PORT))) with open(server_js, "r", encoding="utf-8") as f: c = f.read() if "|| 3000" in c: with open(server_js, "w", encoding="utf-8") as f: - f.write(c.replace("|| 3000", "|| 30006")) + f.write(c.replace("|| 3000", "|| %d" % deploy_port)) except Exception: pass zip_path = os.path.join(tempfile.gettempdir(), "soul_devlop.zip") @@ -577,8 +587,9 @@ def upload_zip_and_extract_to_dist2(cfg, zip_path): def run_pnpm_install_in_dist2(cfg): - """服务器 dist2 内执行 pnpm install(devlop 模式)""" - print("[4/7] 服务器 dist2 内执行 pnpm install ...") + """服务器 dist2 内执行 pnpm install,阻塞等待完成后再返回(改目录前必须完成)""" + print("[4/7] 服务器 dist2 内执行 pnpm install(等待完成后再切换目录)...") + sys.stdout.flush() client = paramiko.SSHClient() client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) try: @@ -596,7 +607,7 @@ def run_pnpm_install_in_dist2(cfg): err = stderr.read().decode("utf-8", errors="replace").strip() if stdout.channel.recv_exit_status() != 0: return False, "pnpm install 失败\n" + (err or out) - print(" [成功] pnpm install 完成") + print(" [成功] dist2 内 pnpm install 已执行完成,可安全切换目录") return True, None except Exception as e: return False, str(e) @@ -673,10 +684,12 @@ def main(): os.remove(zip_path) except Exception: pass + # 必须在 dist2 内 pnpm install 执行完成后再切换目录 ok, err = run_pnpm_install_in_dist2(cfg) if not ok: print(" [失败] %s" % (err or "pnpm install 失败")) return 1 + # install 已完成,再执行 dist→dist1、dist2→dist 切换 if not remote_swap_dist_and_restart(cfg): return 1 print("") diff --git a/scripts/devlop.py b/scripts/devlop.py index 96481342..56a46346 100644 --- a/scripts/devlop.py +++ b/scripts/devlop.py @@ -1,23 +1,5 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -""" -Soul 创业派对 - 统一部署脚本(支持两种模式) - -模式一 devlop(默认):dist 切换方式 - 本地 pnpm build → 打包 zip → 上传解压到 dist2 → 宝塔暂停 → dist→dist1, dist2→dist → 重启 - 用法: python scripts/devlop.py [--no-build] - -模式二 deploy:直接覆盖方式 - 本地打包 tar.gz → SSH 上传解压到项目目录 → 宝塔 API 重启 - 用法: python scripts/devlop.py --mode deploy [--no-build] [--no-upload] [--no-api] - -环境变量(可选): - DEPLOY_HOST / DEPLOY_USER / DEPLOY_PASSWORD / DEPLOY_SSH_KEY - DEPLOY_PROJECT_PATH # deploy 模式项目路径,默认 /www/wwwroot/soul - DEVOP_BASE_PATH # devlop 模式目录,默认 /www/wwwroot/auto-devlop/soul - BAOTA_PANEL_URL / BAOTA_API_KEY / DEPLOY_PM2_APP - DEPLOY_PORT / DEPLOY_NODE_VERSION / DEPLOY_NODE_PATH -""" from __future__ import print_function @@ -52,6 +34,12 @@ except ImportError: # ==================== 配置 ==================== +# 端口统一从环境变量 DEPLOY_PORT 读取,未设置时使用此默认值 +DEPLOY_PM2_APP = "soul" +DEFAULT_DEPLOY_PORT = 30006 +DEPLOY_PROJECT_PATH = "/www/wwwroot/soul" +DEPLOY_SITE_URL = "https://soul.quwanzhi.com" + def get_cfg(): """获取基础部署配置(deploy 模式与 devlop 共用 SSH/宝塔)""" return { @@ -59,12 +47,12 @@ def get_cfg(): "user": os.environ.get("DEPLOY_USER", "root"), "password": os.environ.get("DEPLOY_PASSWORD", "Zhiqun1984"), "ssh_key": os.environ.get("DEPLOY_SSH_KEY", ""), - "project_path": os.environ.get("DEPLOY_PROJECT_PATH", "/www/wwwroot/soul"), + "project_path": os.environ.get("DEPLOY_PROJECT_PATH", DEPLOY_PROJECT_PATH), "panel_url": os.environ.get("BAOTA_PANEL_URL", "https://42.194.232.22:9988"), "api_key": os.environ.get("BAOTA_API_KEY", "hsAWqFSi0GOCrunhmYdkxy92tBXfqYjd"), - "pm2_name": os.environ.get("DEPLOY_PM2_APP", "soul"), - "site_url": os.environ.get("DEPLOY_SITE_URL", "https://soul.quwanzhi.com"), - "port": int(os.environ.get("DEPLOY_PORT", "30006")), + "pm2_name": os.environ.get("DEPLOY_PM2_APP", DEPLOY_PM2_APP), + "site_url": os.environ.get("DEPLOY_SITE_URL", DEPLOY_SITE_URL), + "port": int(os.environ.get("DEPLOY_PORT", str(DEFAULT_DEPLOY_PORT))), "node_version": os.environ.get("DEPLOY_NODE_VERSION", "v22.14.0"), "node_path": os.environ.get("DEPLOY_NODE_PATH", "/www/server/nodejs/v22.14.0/bin"), } @@ -73,7 +61,7 @@ def get_cfg(): def get_cfg_devlop(): """devlop 模式配置:在基础配置上增加 base_path / dist / dist2""" cfg = get_cfg().copy() - cfg["base_path"] = os.environ.get("DEVOP_BASE_PATH", "/www/wwwroot/auto-devlop/soul") + cfg["base_path"] = os.environ.get("DEVOP_BASE_PATH", "/www/wwwroot/auto-devlop/soulTest") cfg["dist_path"] = cfg["base_path"] + "/dist" cfg["dist2_path"] = cfg["base_path"] + "/dist2" return cfg @@ -152,7 +140,9 @@ def restart_node_project(panel_url, api_key, pm2_name): return False -def add_or_update_node_project(panel_url, api_key, pm2_name, project_path, port=30006, node_path=None): +def add_or_update_node_project(panel_url, api_key, pm2_name, project_path, port=None, node_path=None): + if port is None: + port = int(os.environ.get("DEPLOY_PORT", str(DEFAULT_DEPLOY_PORT))) port_env = "PORT=%d " % port run_cmd = port_env + ("%s/node server.js" % node_path if node_path else "node server.js") payload = {"name": pm2_name, "path": project_path, "run_cmd": run_cmd, "port": str(port)} @@ -364,8 +354,8 @@ def upload_and_extract(cfg, tarball_path): else: client.connect(cfg["host"], username=cfg["user"], password=cfg["password"], timeout=15) sftp = client.open_sftp() - remote_tar = "/tmp/soul_deploy.tar.gz" - remote_script = "/tmp/soul_deploy_extract.sh" + remote_tar = "/tmp/soulTest_deploy.tar.gz" + remote_script = "/tmp/soulTest_deploy_extract.sh" sftp.put(tarball_path, remote_tar) node_path = cfg.get("node_path", "/www/server/nodejs/v22.14.0/bin") project_path = cfg["project_path"] @@ -402,7 +392,7 @@ def deploy_via_baota_api(cfg): panel_url, api_key, pm2_name = cfg["panel_url"], cfg["api_key"], cfg["pm2_name"] project_path = cfg["project_path"] node_path = cfg.get("node_path", "/www/server/nodejs/v22.14.0/bin") - port = cfg.get("port", 30006) + port = cfg["port"] if not get_node_project_status(panel_url, api_key, pm2_name): add_or_update_node_project(panel_url, api_key, pm2_name, project_path, port, node_path) @@ -482,11 +472,12 @@ def pack_standalone_zip(root): server_js = os.path.join(staging, "server.js") if os.path.isfile(server_js): try: + deploy_port = int(os.environ.get("DEPLOY_PORT", str(DEFAULT_DEPLOY_PORT))) with open(server_js, "r", encoding="utf-8") as f: c = f.read() if "|| 3000" in c: with open(server_js, "w", encoding="utf-8") as f: - f.write(c.replace("|| 3000", "|| 30006")) + f.write(c.replace("|| 3000", "|| %d" % deploy_port)) except Exception: pass zip_path = os.path.join(tempfile.gettempdir(), "soul_devlop.zip") @@ -532,7 +523,7 @@ def upload_zip_and_extract_to_dist2(cfg, zip_path): client.connect(cfg["host"], username=cfg["user"], password=cfg["password"], timeout=30, banner_timeout=30) print(" [OK] SSH 已连接,正在上传 zip(%.1f MB)..." % zip_size_mb) sys.stdout.flush() - remote_zip = cfg["base_path"].rstrip("/") + "/soul_devlop.zip" + remote_zip = cfg["base_path"].rstrip("/") + "/soulTest_devlop.zip" sftp = client.open_sftp() # 上传进度:每 5MB 打印一次 chunk_mb = 5.0 @@ -577,8 +568,9 @@ def upload_zip_and_extract_to_dist2(cfg, zip_path): def run_pnpm_install_in_dist2(cfg): - """服务器 dist2 内执行 pnpm install(devlop 模式)""" - print("[4/7] 服务器 dist2 内执行 pnpm install ...") + """服务器 dist2 内执行 pnpm install,阻塞等待完成后再返回(改目录前必须完成)""" + print("[4/7] 服务器 dist2 内执行 pnpm install(等待完成后再切换目录)...") + sys.stdout.flush() client = paramiko.SSHClient() client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) try: @@ -596,7 +588,7 @@ def run_pnpm_install_in_dist2(cfg): err = stderr.read().decode("utf-8", errors="replace").strip() if stdout.channel.recv_exit_status() != 0: return False, "pnpm install 失败\n" + (err or out) - print(" [成功] pnpm install 完成") + print(" [成功] dist2 内 pnpm install 已执行完成,可安全切换目录") return True, None except Exception as e: return False, str(e) @@ -673,10 +665,12 @@ def main(): os.remove(zip_path) except Exception: pass + # 必须在 dist2 内 pnpm install 执行完成后再切换目录 ok, err = run_pnpm_install_in_dist2(cfg) if not ok: print(" [失败] %s" % (err or "pnpm install 失败")) return 1 + # install 已完成,再执行 dist→dist1、dist2→dist 切换 if not remote_swap_dist_and_restart(cfg): return 1 print("") diff --git a/scripts/merge-kbone-to-miniprogram.js b/scripts/merge-kbone-to-miniprogram.js index 677b0493..e3935732 100644 --- a/scripts/merge-kbone-to-miniprogram.js +++ b/scripts/merge-kbone-to-miniprogram.js @@ -1,17 +1,17 @@ /** - * 将 newpp 构建产物 dist/mp 合并到项目根 miniprogram/ - * 保留 miniprogram 壳:custom-tab-bar、project.config.json、sitemap.json、app.js 的 globalData/request 等需手动合并 + * 将 newapp 构建产物 dist/mp 合并到项目根 miniprogram/ + * 保留 miniprogram 壳:custom-tab-bar、project.config.json、sitemap.json、app.js(不覆盖壳的 app.js) * 用法:node scripts/merge-kbone-to-miniprogram.js */ const fs = require('fs') const path = require('path') const root = path.resolve(__dirname, '..') -const distMp = path.join(root, 'newpp', 'dist', 'mp') +const distMp = path.join(root, 'newapp', 'dist', 'mp') const miniprogram = path.join(root, 'miniprogram') if (!fs.existsSync(distMp)) { - console.error('未找到 newpp/dist/mp,请先在 newpp 目录执行: npm run build:mp') + console.error('未找到 newapp/dist/mp,请先在 newapp 目录执行: npm run build:mp') process.exit(1) } if (!fs.existsSync(miniprogram)) { @@ -34,7 +34,7 @@ function copyDir(src, dest) { } // 要保留的 miniprogram 文件/目录(合并前备份,合并后覆盖回去) -const keep = ['custom-tab-bar', 'project.config.json', 'sitemap.json'] +const keep = ['custom-tab-bar', 'project.config.json', 'sitemap.json', 'app.js'] const backupDir = path.join(root, '.miniprogram-backup') if (!fs.existsSync(backupDir)) fs.mkdirSync(backupDir, { recursive: true }) @@ -82,5 +82,4 @@ for (const name of keep) { } } -console.log('已合并 newpp/dist/mp -> miniprogram/,并保留 custom-tab-bar、project.config.json、sitemap.json') -console.log('请手动合并 app.js 的 globalData、request、loadFeatureConfig 等逻辑到 kbone 生成的 app.js') +console.log('已合并 newapp/dist/mp -> miniprogram/,并保留 custom-tab-bar、project.config.json、sitemap.json、app.js')