@coze-arch/cli 0.0.36-alpha.844e20 → 0.0.36-alpha.98d71c
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/deploy.md +13 -10
- package/lib/__templates__/expo/.cozeproj/scripts/dev_run.sh +73 -18
- package/lib/__templates__/nextjs/scripts/dev.sh +10 -1
- package/lib/__templates__/nextjs/template.config.js +3 -1
- package/lib/__templates__/phaser/_gitignore +1 -0
- package/lib/__templates__/phaser/scripts/dev.sh +74 -3
- package/lib/__templates__/phaser/scripts/spawn-detached.cjs +29 -0
- package/lib/__templates__/phaser/scripts/start.sh +74 -3
- package/lib/__templates__/taro/.cozeproj/scripts/dev_run.sh +20 -14
- package/lib/__templates__/vite/template.config.js +3 -1
- package/lib/cli.js +631 -524
- package/lib/deploy/package-project.js +6 -0
- package/lib/deploy/package-project.py +3 -0
- package/package.json +1 -1
package/docs/deploy.md
CHANGED
|
@@ -54,7 +54,7 @@ coze-dev deploy code deploy logs|log-count|count|build-log|config
|
|
|
54
54
|
coze-dev deploy code domain list [--unit-type dev|prod]
|
|
55
55
|
coze-dev deploy code domain add <domain>
|
|
56
56
|
coze-dev deploy code domain set-prefix <prefix> --app-id <app_id> --yes
|
|
57
|
-
coze-dev deploy code mini-program status|bind|submit-code|unbind
|
|
57
|
+
coze-dev deploy code mini-program status|bind|submit-code|unbind|auth|preview
|
|
58
58
|
```
|
|
59
59
|
|
|
60
60
|
`code app list` 是真分页:一次只返回一页(`--page-size` 默认 20、上限 100),`has_more` 为真时用返回的 `next_page_token` 取下一页;要一次拿到名下全部应用用 `--all`,它会自己翻到底。
|
|
@@ -101,21 +101,17 @@ V2 接口的所有参数均通过 POST JSON body 发送,不会放入 URL。确
|
|
|
101
101
|
`scripts/package-project.py` 仍随源码和 npm 包保留,仅作为 Python 参考备份与行为对照,
|
|
102
102
|
生产打包链路不会调用 Python。
|
|
103
103
|
|
|
104
|
-
|
|
105
|
-
|
|
104
|
+
环境变量以当前线上成功版本的 Secret 快照为准,CLI 不再读取源码根目录的 `.env`。
|
|
105
|
+
更新服务或小程序时,CLI 会读取线上快照中的用户变量并重新加密上传;首次部署没有线上
|
|
106
|
+
快照,因此使用空的用户变量集合。平台系统变量由 Secret 服务管理,不作为用户变量上传。
|
|
106
107
|
|
|
107
|
-
|
|
108
|
-
`--source-root` 指定项目目录;这些命令不会请求部署服务:
|
|
108
|
+
通过 `app_id` 查看当前线上版本的环境变量及快照状态:
|
|
109
109
|
|
|
110
110
|
```bash
|
|
111
111
|
coze-dev deploy code env list
|
|
112
|
-
coze-dev deploy code env
|
|
113
|
-
coze-dev deploy code env delete API_KEY
|
|
114
|
-
coze-dev deploy code env list --source-root ./projects/example
|
|
112
|
+
coze-dev deploy code env list --app-id 7672644451805691910
|
|
115
113
|
```
|
|
116
114
|
|
|
117
|
-
`env set` 和 `env delete` 不允许操作 `COZE_` 前缀的保留变量。
|
|
118
|
-
|
|
119
115
|
小程序部署可重复指定渠道:微信为 `10000127`,抖音为 `10000126`:
|
|
120
116
|
|
|
121
117
|
```bash
|
|
@@ -123,6 +119,13 @@ coze-dev deploy code deploy --source-root . --application-type mini_program \
|
|
|
123
119
|
--connector-id 10000127 --yes --wait
|
|
124
120
|
```
|
|
125
121
|
|
|
122
|
+
小程序预览先完成授权,再执行 preview。preview 会打包微信的 dist/ 或抖音的 dist-tt/ 并上传。Project ID 可通过参数传入或从 .coze 自动读取。
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
coze-dev deploy code mini-program auth --platform wechat --mini-app-id wx123 --source-root .
|
|
126
|
+
coze-dev deploy code mini-program preview --platform wechat --mini-app-id wx123 --source-root .
|
|
127
|
+
```
|
|
128
|
+
|
|
126
129
|
绑定小程序 AppID 时,默认从源码目录 `.coze [project]` 读取该目录的 `project_id`:
|
|
127
130
|
|
|
128
131
|
```bash
|
|
@@ -1,17 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
1
3
|
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
2
4
|
PREVIEW_DIR="/source/preview"
|
|
3
5
|
LOG_DIR="${COZE_LOG_DIR:-$ROOT_DIR/logs}"
|
|
4
6
|
LOG_CLIENT_FILE="$LOG_DIR/client.log"
|
|
7
|
+
LOG_SERVER_FILE="$LOG_DIR/server.log"
|
|
5
8
|
mkdir -p "$LOG_DIR"
|
|
6
9
|
|
|
7
10
|
# ==================== 配置项 ====================
|
|
8
11
|
# Server 服务配置
|
|
9
12
|
SERVER_HOST="0.0.0.0"
|
|
10
|
-
SERVER_PORT="9091"
|
|
13
|
+
SERVER_PORT="${SERVER_PORT:-9091}"
|
|
11
14
|
# Expo 项目配置
|
|
12
15
|
EXPO_HOST="0.0.0.0"
|
|
13
16
|
EXPO_DIR="expo"
|
|
14
|
-
EXPO_PORT="5000"
|
|
17
|
+
EXPO_PORT="${EXPO_PORT:-5000}"
|
|
15
18
|
WEB_URL="${COZE_PROJECT_DOMAIN_DEFAULT:-http://127.0.0.1:${SERVER_PORT}}"
|
|
16
19
|
ASSUME_YES="1"
|
|
17
20
|
EXPO_PUBLIC_BACKEND_BASE_URL="${EXPO_PUBLIC_BACKEND_BASE_URL:-$WEB_URL}"
|
|
@@ -22,6 +25,7 @@ export EXPO_PUBLIC_BACKEND_BASE_URL EXPO_PACKAGER_PROXY_URL EXPO_PUBLIC_COZE_PRO
|
|
|
22
25
|
# 运行时变量(为避免 set -u 的未绑定错误,预置为空)
|
|
23
26
|
SERVER_PID=""
|
|
24
27
|
EXPO_PID=""
|
|
28
|
+
PNPM_BIN=""
|
|
25
29
|
|
|
26
30
|
# ==================== 工具函数 ====================
|
|
27
31
|
check_command() {
|
|
@@ -112,22 +116,67 @@ wait_port_connectable() {
|
|
|
112
116
|
return 1
|
|
113
117
|
}
|
|
114
118
|
|
|
115
|
-
|
|
116
|
-
|
|
119
|
+
# coze-daemon 会在 runtime shell 退出时清理原进程组。
|
|
120
|
+
# 通过 Node detached spawn 创建独立 session/进程组,并将 stdio 直接写入日志。
|
|
121
|
+
spawn_detached() {
|
|
122
|
+
local cwd="$1"
|
|
123
|
+
local log_file="$2"
|
|
124
|
+
shift 2
|
|
125
|
+
|
|
126
|
+
node - "$cwd" "$log_file" "$@" <<'NODE'
|
|
127
|
+
const fs = require('node:fs');
|
|
128
|
+
const { spawn } = require('node:child_process');
|
|
117
129
|
|
|
118
|
-
|
|
130
|
+
const [cwd, logFile, command, ...args] = process.argv.slice(2);
|
|
131
|
+
if (!cwd || !logFile || !command) {
|
|
132
|
+
throw new Error('spawn_detached 缺少 cwd、log_file 或 command');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const logFd = fs.openSync(logFile, 'a');
|
|
136
|
+
try {
|
|
137
|
+
const child = spawn(command, args, {
|
|
138
|
+
cwd,
|
|
139
|
+
detached: true,
|
|
140
|
+
env: process.env,
|
|
141
|
+
stdio: ['ignore', logFd, logFd],
|
|
142
|
+
});
|
|
143
|
+
child.unref();
|
|
144
|
+
process.stdout.write(String(child.pid));
|
|
145
|
+
} finally {
|
|
146
|
+
fs.closeSync(logFd);
|
|
147
|
+
}
|
|
148
|
+
NODE
|
|
149
|
+
}
|
|
119
150
|
|
|
151
|
+
stop_detached() {
|
|
152
|
+
local pid="${1:-}"
|
|
153
|
+
if [ -z "$pid" ]; then
|
|
154
|
+
return
|
|
155
|
+
fi
|
|
156
|
+
|
|
157
|
+
kill -TERM -- "-$pid" 2>/dev/null || kill -TERM "$pid" 2>/dev/null || true
|
|
158
|
+
sleep 1
|
|
159
|
+
kill -KILL -- "-$pid" 2>/dev/null || true
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
start_expo() {
|
|
163
|
+
local offline="${1:-0}"
|
|
164
|
+
local -a expo_env
|
|
165
|
+
expo_env=(
|
|
166
|
+
"EXPO_NO_DEPENDENCY_VALIDATION=1"
|
|
167
|
+
"EXPO_PUBLIC_BACKEND_BASE_URL=$EXPO_PUBLIC_BACKEND_BASE_URL"
|
|
168
|
+
"EXPO_PACKAGER_PROXY_URL=$EXPO_PACKAGER_PROXY_URL"
|
|
169
|
+
"EXPO_PUBLIC_COZE_PROJECT_ID=$EXPO_PUBLIC_COZE_PROJECT_ID"
|
|
170
|
+
)
|
|
120
171
|
if [ "$offline" = "1" ]; then
|
|
121
|
-
(
|
|
122
|
-
nohup npx expo start --clear --port "$EXPO_PORT" 2>&1 | pipe_to_log "CLIENT" "$LOG_CLIENT_FILE" ) &
|
|
123
|
-
else
|
|
124
|
-
( EXPO_NO_DEPENDENCY_VALIDATION=1 EXPO_PUBLIC_BACKEND_BASE_URL="$EXPO_PUBLIC_BACKEND_BASE_URL" EXPO_PACKAGER_PROXY_URL="$EXPO_PACKAGER_PROXY_URL" EXPO_PUBLIC_COZE_PROJECT_ID="$EXPO_PUBLIC_COZE_PROJECT_ID" \
|
|
125
|
-
nohup npx expo start --clear --port "$EXPO_PORT" 2>&1 | pipe_to_log "CLIENT" "$LOG_CLIENT_FILE" ) &
|
|
172
|
+
expo_env+=("EXPO_OFFLINE=1")
|
|
126
173
|
fi
|
|
127
|
-
EXPO_PID=$!
|
|
128
|
-
disown $EXPO_PID 2>/dev/null || true
|
|
129
174
|
|
|
130
|
-
|
|
175
|
+
EXPO_PID="$(spawn_detached \
|
|
176
|
+
"$ROOT_DIR/client" \
|
|
177
|
+
"$LOG_CLIENT_FILE" \
|
|
178
|
+
/usr/bin/env "${expo_env[@]}" \
|
|
179
|
+
"$PNPM_BIN" exec expo start --clear --port "$EXPO_PORT")"
|
|
131
180
|
}
|
|
132
181
|
|
|
133
182
|
detect_expo_fetch_failed() {
|
|
@@ -168,6 +217,8 @@ check_command "npm"
|
|
|
168
217
|
check_command "pnpm"
|
|
169
218
|
check_command "lsof"
|
|
170
219
|
check_command "bash"
|
|
220
|
+
check_command "node"
|
|
221
|
+
PNPM_BIN="$(command -v pnpm)"
|
|
171
222
|
|
|
172
223
|
# 端口占用预检查与处理
|
|
173
224
|
ensure_port SERVER_PORT "$SERVER_PORT"
|
|
@@ -175,9 +226,12 @@ ensure_port EXPO_PORT "$EXPO_PORT"
|
|
|
175
226
|
|
|
176
227
|
echo "==================== 启动 server 服务 ===================="
|
|
177
228
|
echo "正在执行:pnpm run dev (server)"
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
229
|
+
: > "$LOG_SERVER_FILE"
|
|
230
|
+
export SERVER_PORT
|
|
231
|
+
SERVER_PID="$(spawn_detached \
|
|
232
|
+
"$ROOT_DIR/server" \
|
|
233
|
+
/dev/null \
|
|
234
|
+
"$PNPM_BIN" run dev)"
|
|
181
235
|
if [ -z "${SERVER_PID}" ]; then
|
|
182
236
|
echo "无法获取 server 后台进程 PID"
|
|
183
237
|
fi
|
|
@@ -185,11 +239,12 @@ echo "server 服务已启动,进程 ID:${SERVER_PID:-unknown}"
|
|
|
185
239
|
|
|
186
240
|
echo "==================== 启动 Expo 项目 ===================="
|
|
187
241
|
echo "开始启动 Expo 服务,端口 ${EXPO_PORT}"
|
|
242
|
+
: > "$LOG_CLIENT_FILE"
|
|
188
243
|
start_expo 0
|
|
189
244
|
if detect_expo_fetch_failed 8; then
|
|
190
245
|
echo "Expo 启动检测到网络错误:TypeError: fetch failed,启用离线模式重试"
|
|
191
|
-
|
|
192
|
-
: > "$
|
|
246
|
+
stop_detached "$EXPO_PID"
|
|
247
|
+
: > "$LOG_CLIENT_FILE"
|
|
193
248
|
start_expo 1
|
|
194
249
|
fi
|
|
195
250
|
# 输出以下环境变量,确保 Expo 项目能正确连接到 Server 服务
|
|
@@ -14,6 +14,9 @@ DEPLOY_RUN_PORT="${DEPLOY_RUN_PORT:-${PORT}}"
|
|
|
14
14
|
|
|
15
15
|
cd "${COZE_WORKSPACE_PATH}"
|
|
16
16
|
|
|
17
|
+
LOG_DIR="${COZE_LOG_DIR:-${COZE_WORKSPACE_PATH}/logs}"
|
|
18
|
+
LOG_SERVER_FILE="${LOG_DIR}/server.log"
|
|
19
|
+
|
|
17
20
|
kill_port_if_listening() {
|
|
18
21
|
local pids
|
|
19
22
|
pids=$(ss -H -lntp 2>/dev/null | awk -v port="${DEPLOY_RUN_PORT}" '$4 ~ ":"port"$"' | grep -o 'pid=[0-9]*' | cut -d= -f2 | paste -sd' ' - || true)
|
|
@@ -36,4 +39,10 @@ echo "Clearing port ${DEPLOY_RUN_PORT} before start."
|
|
|
36
39
|
kill_port_if_listening
|
|
37
40
|
echo "Starting HTTP service on port ${DEPLOY_RUN_PORT} for dev..."
|
|
38
41
|
|
|
39
|
-
|
|
42
|
+
mkdir -p "${LOG_DIR}"
|
|
43
|
+
: > "${LOG_SERVER_FILE}"
|
|
44
|
+
|
|
45
|
+
# 保持服务为前台进程,让外层 coze-dev dev 的 PID 在整个服务生命周期内有效。
|
|
46
|
+
# 同时通过 tee 将同一份输出写入 logs/server.log。
|
|
47
|
+
exec env PORT="${DEPLOY_RUN_PORT}" pnpm tsx watch src/server.ts \
|
|
48
|
+
> >(tee -a "${LOG_SERVER_FILE}") 2>&1
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { spawn } from 'child_process';
|
|
4
4
|
import { resolve, join, basename } from 'path';
|
|
5
5
|
import { appendFileSync, openSync, closeSync, mkdirSync } from 'fs';
|
|
6
|
+
import { homedir } from 'os';
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
|
|
@@ -74,7 +75,8 @@ const config = {
|
|
|
74
75
|
const projectRoot = resolve(outputPath);
|
|
75
76
|
|
|
76
77
|
// Determine log directory
|
|
77
|
-
const
|
|
78
|
+
const cozeHome = process.env.COZE_HOME || join(homedir(), '.coze');
|
|
79
|
+
const logDir = process.env.COZE_LOG_DIR || join(cozeHome, 'logs');
|
|
78
80
|
mkdirSync(logDir, { recursive: true });
|
|
79
81
|
|
|
80
82
|
// Use project name in log file to avoid conflicts
|
|
@@ -2,10 +2,81 @@
|
|
|
2
2
|
set -Eeuo pipefail
|
|
3
3
|
|
|
4
4
|
COZE_WORKSPACE_PATH="${COZE_WORKSPACE_PATH:-$(pwd)}"
|
|
5
|
-
PORT="${
|
|
5
|
+
PORT="${PORT:-<%= port %>}"
|
|
6
|
+
DEPLOY_RUN_PORT="${DEPLOY_RUN_PORT:-${PORT}}"
|
|
6
7
|
cd "${COZE_WORKSPACE_PATH}"
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
get_listening_pids() {
|
|
10
|
+
local port="$1"
|
|
11
|
+
|
|
12
|
+
if command -v lsof >/dev/null 2>&1; then
|
|
13
|
+
lsof -tiTCP:"${port}" -sTCP:LISTEN 2>/dev/null || true
|
|
14
|
+
elif command -v ss >/dev/null 2>&1; then
|
|
15
|
+
ss -H -lntp 2>/dev/null \
|
|
16
|
+
| awk -v port="${port}" '$4 ~ ":"port"$"' \
|
|
17
|
+
| grep -o 'pid=[0-9]*' \
|
|
18
|
+
| cut -d= -f2 \
|
|
19
|
+
| sort -u || true
|
|
20
|
+
else
|
|
21
|
+
echo "Warning: neither lsof nor ss found, cannot check port ${port}." >&2
|
|
22
|
+
fi
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
kill_port_if_listening() {
|
|
26
|
+
local port="$1"
|
|
27
|
+
local pids
|
|
28
|
+
|
|
29
|
+
pids="$(get_listening_pids "${port}")"
|
|
30
|
+
if [[ -z "${pids}" ]]; then
|
|
31
|
+
echo "Port ${port} is free."
|
|
32
|
+
return
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
echo "Port ${port} is in use by PIDs: ${pids//$'\n'/ } (SIGKILL)"
|
|
36
|
+
echo "${pids}" | xargs kill -9 2>/dev/null || true
|
|
37
|
+
sleep 1
|
|
38
|
+
|
|
39
|
+
pids="$(get_listening_pids "${port}")"
|
|
40
|
+
if [[ -n "${pids}" ]]; then
|
|
41
|
+
echo "Failed to clear port ${port}; remaining PIDs: ${pids//$'\n'/ }." >&2
|
|
42
|
+
return 1
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
echo "Port ${port} cleared."
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
echo "Clearing port ${DEPLOY_RUN_PORT} before start."
|
|
49
|
+
kill_port_if_listening "${DEPLOY_RUN_PORT}"
|
|
50
|
+
|
|
51
|
+
<% if (process.env.NODE_ENV === 'test') { %>
|
|
52
|
+
# Keep the process attached in tests so the test runner can collect logs and stop it.
|
|
53
|
+
exec env COZE_PHASER_GAME_ENV=DEV pnpm vite \
|
|
54
|
+
--port "${DEPLOY_RUN_PORT}" \
|
|
10
55
|
--host 127.0.0.1 \
|
|
11
56
|
--strictPort
|
|
57
|
+
<% } else { %>
|
|
58
|
+
LOG_DIR="${COZE_WORKSPACE_PATH}/logs"
|
|
59
|
+
LOG_FILE="${LOG_DIR}/phaser-dev.log"
|
|
60
|
+
PID_FILE="${LOG_DIR}/phaser-dev.pid"
|
|
61
|
+
mkdir -p "${LOG_DIR}"
|
|
62
|
+
|
|
63
|
+
echo "Starting Phaser dev server on port ${DEPLOY_RUN_PORT}..."
|
|
64
|
+
server_pid="$(COZE_PHASER_GAME_ENV=DEV node scripts/spawn-detached.cjs \
|
|
65
|
+
"${LOG_FILE}" pnpm vite \
|
|
66
|
+
--port "${DEPLOY_RUN_PORT}" \
|
|
67
|
+
--host 127.0.0.1 \
|
|
68
|
+
--strictPort)"
|
|
69
|
+
echo "${server_pid}" > "${PID_FILE}"
|
|
70
|
+
|
|
71
|
+
sleep 1
|
|
72
|
+
if ! kill -0 "${server_pid}" 2>/dev/null; then
|
|
73
|
+
echo "Phaser dev server failed to start. See ${LOG_FILE}." >&2
|
|
74
|
+
tail -n 20 "${LOG_FILE}" >&2 || true
|
|
75
|
+
rm -f "${PID_FILE}"
|
|
76
|
+
exit 1
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
echo "Phaser dev server started (PID: ${server_pid})."
|
|
80
|
+
echo "Log file: ${LOG_FILE}"
|
|
81
|
+
echo "PID file: ${PID_FILE}"
|
|
82
|
+
<% } %>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const fs = require('node:fs');
|
|
2
|
+
const { spawn } = require('node:child_process');
|
|
3
|
+
|
|
4
|
+
const [logFile, command, ...args] = process.argv.slice(2);
|
|
5
|
+
|
|
6
|
+
if (!logFile || !command) {
|
|
7
|
+
process.stderr.write(
|
|
8
|
+
'Usage: node scripts/spawn-detached.cjs <log-file> <command> [...args]\n',
|
|
9
|
+
);
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const logFd = fs.openSync(logFile, 'a');
|
|
14
|
+
const child = spawn(command, args, {
|
|
15
|
+
cwd: process.cwd(),
|
|
16
|
+
detached: process.platform !== 'win32',
|
|
17
|
+
env: process.env,
|
|
18
|
+
stdio: ['ignore', logFd, logFd],
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
fs.closeSync(logFd);
|
|
22
|
+
child.unref();
|
|
23
|
+
|
|
24
|
+
if (!child.pid) {
|
|
25
|
+
process.stderr.write('Failed to start detached process.\n');
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
process.stdout.write(String(child.pid));
|
|
@@ -2,10 +2,81 @@
|
|
|
2
2
|
set -Eeuo pipefail
|
|
3
3
|
|
|
4
4
|
COZE_WORKSPACE_PATH="${COZE_WORKSPACE_PATH:-$(pwd)}"
|
|
5
|
-
PORT="${
|
|
5
|
+
PORT="${PORT:-<%= port %>}"
|
|
6
|
+
DEPLOY_RUN_PORT="${DEPLOY_RUN_PORT:-${PORT}}"
|
|
6
7
|
cd "${COZE_WORKSPACE_PATH}"
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
get_listening_pids() {
|
|
10
|
+
local port="$1"
|
|
11
|
+
|
|
12
|
+
if command -v lsof >/dev/null 2>&1; then
|
|
13
|
+
lsof -tiTCP:"${port}" -sTCP:LISTEN 2>/dev/null || true
|
|
14
|
+
elif command -v ss >/dev/null 2>&1; then
|
|
15
|
+
ss -H -lntp 2>/dev/null \
|
|
16
|
+
| awk -v port="${port}" '$4 ~ ":"port"$"' \
|
|
17
|
+
| grep -o 'pid=[0-9]*' \
|
|
18
|
+
| cut -d= -f2 \
|
|
19
|
+
| sort -u || true
|
|
20
|
+
else
|
|
21
|
+
echo "Warning: neither lsof nor ss found, cannot check port ${port}." >&2
|
|
22
|
+
fi
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
kill_port_if_listening() {
|
|
26
|
+
local port="$1"
|
|
27
|
+
local pids
|
|
28
|
+
|
|
29
|
+
pids="$(get_listening_pids "${port}")"
|
|
30
|
+
if [[ -z "${pids}" ]]; then
|
|
31
|
+
echo "Port ${port} is free."
|
|
32
|
+
return
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
echo "Port ${port} is in use by PIDs: ${pids//$'\n'/ } (SIGKILL)"
|
|
36
|
+
echo "${pids}" | xargs kill -9 2>/dev/null || true
|
|
37
|
+
sleep 1
|
|
38
|
+
|
|
39
|
+
pids="$(get_listening_pids "${port}")"
|
|
40
|
+
if [[ -n "${pids}" ]]; then
|
|
41
|
+
echo "Failed to clear port ${port}; remaining PIDs: ${pids//$'\n'/ }." >&2
|
|
42
|
+
return 1
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
echo "Port ${port} cleared."
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
echo "Clearing port ${DEPLOY_RUN_PORT} before start."
|
|
49
|
+
kill_port_if_listening "${DEPLOY_RUN_PORT}"
|
|
50
|
+
|
|
51
|
+
<% if (process.env.NODE_ENV === 'test') { %>
|
|
52
|
+
# Keep the process attached in tests so the test runner can collect logs and stop it.
|
|
53
|
+
exec env COZE_PHASER_GAME_ENV=PROD pnpm vite preview \
|
|
54
|
+
--port "${DEPLOY_RUN_PORT}" \
|
|
10
55
|
--host 127.0.0.1 \
|
|
11
56
|
--strictPort
|
|
57
|
+
<% } else { %>
|
|
58
|
+
LOG_DIR="${COZE_WORKSPACE_PATH}/logs"
|
|
59
|
+
LOG_FILE="${LOG_DIR}/phaser-start.log"
|
|
60
|
+
PID_FILE="${LOG_DIR}/phaser-start.pid"
|
|
61
|
+
mkdir -p "${LOG_DIR}"
|
|
62
|
+
|
|
63
|
+
echo "Starting Phaser production server on port ${DEPLOY_RUN_PORT}..."
|
|
64
|
+
server_pid="$(COZE_PHASER_GAME_ENV=PROD node scripts/spawn-detached.cjs \
|
|
65
|
+
"${LOG_FILE}" pnpm vite preview \
|
|
66
|
+
--port "${DEPLOY_RUN_PORT}" \
|
|
67
|
+
--host 127.0.0.1 \
|
|
68
|
+
--strictPort)"
|
|
69
|
+
echo "${server_pid}" > "${PID_FILE}"
|
|
70
|
+
|
|
71
|
+
sleep 1
|
|
72
|
+
if ! kill -0 "${server_pid}" 2>/dev/null; then
|
|
73
|
+
echo "Phaser production server failed to start. See ${LOG_FILE}." >&2
|
|
74
|
+
tail -n 20 "${LOG_FILE}" >&2 || true
|
|
75
|
+
rm -f "${PID_FILE}"
|
|
76
|
+
exit 1
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
echo "Phaser production server started (PID: ${server_pid})."
|
|
80
|
+
echo "Log file: ${LOG_FILE}"
|
|
81
|
+
echo "PID file: ${PID_FILE}"
|
|
82
|
+
<% } %>
|
|
@@ -8,6 +8,9 @@ cd "${COZE_WORKSPACE_PATH}"
|
|
|
8
8
|
# PID 文件,用于追踪上一次启动的进程树
|
|
9
9
|
# ---------------------------------------------------------
|
|
10
10
|
PID_FILE="/tmp/coze-dev-run.pid"
|
|
11
|
+
LOG_DIR="/tmp/coze-logs"
|
|
12
|
+
LOG_FILE="${LOG_DIR}/dev.log"
|
|
13
|
+
DEV_PID=""
|
|
11
14
|
|
|
12
15
|
# ---------------------------------------------------------
|
|
13
16
|
# 工具函数
|
|
@@ -89,7 +92,8 @@ cleanup_previous_run() {
|
|
|
89
92
|
# 2. 安装依赖
|
|
90
93
|
# ---------------------------------------------------------
|
|
91
94
|
echo "📦 Installing dependencies..."
|
|
92
|
-
pnpm
|
|
95
|
+
PNPM_BIN="$(command -v pnpm)"
|
|
96
|
+
"${PNPM_BIN}" install
|
|
93
97
|
echo "✅ Dependencies installed successfully!"
|
|
94
98
|
|
|
95
99
|
# ---------------------------------------------------------
|
|
@@ -108,11 +112,14 @@ kill_port_if_listening "${SERVER_PORT}"
|
|
|
108
112
|
# 4. 退出时自动清理子进程(信号 trap)
|
|
109
113
|
# ---------------------------------------------------------
|
|
110
114
|
cleanup_on_exit() {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
kill
|
|
115
|
+
local exit_code=$?
|
|
116
|
+
trap - EXIT INT TERM HUP
|
|
117
|
+
if [[ -n "${DEV_PID}" ]] && kill -0 "${DEV_PID}" 2>/dev/null; then
|
|
118
|
+
echo "🛑 dev_run.sh exiting, cleaning up child processes..."
|
|
119
|
+
kill_process_tree "${DEV_PID}"
|
|
120
|
+
fi
|
|
114
121
|
rm -f "${PID_FILE}"
|
|
115
|
-
exit
|
|
122
|
+
exit "${exit_code}"
|
|
116
123
|
}
|
|
117
124
|
trap cleanup_on_exit EXIT INT TERM HUP
|
|
118
125
|
|
|
@@ -134,17 +141,16 @@ start_service() {
|
|
|
134
141
|
echo "Starting Taro H5 Dev Server and NestJS Server..."
|
|
135
142
|
|
|
136
143
|
export PORT=${DEPLOY_RUN_PORT}
|
|
137
|
-
|
|
138
|
-
|
|
144
|
+
mkdir -p "${LOG_DIR}"
|
|
145
|
+
: > "${LOG_FILE}"
|
|
139
146
|
|
|
140
|
-
#
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
echo "${
|
|
144
|
-
echo "📝 Dev process started with PID: ${
|
|
147
|
+
# 后台启动以便记录真实 pnpm PID,但脚本持续 wait,保持外层 CLI 生命周期有效。
|
|
148
|
+
"${PNPM_BIN}" dev > >(tee -a "${LOG_FILE}") 2>&1 &
|
|
149
|
+
DEV_PID=$!
|
|
150
|
+
echo "${DEV_PID}" > "${PID_FILE}"
|
|
151
|
+
echo "📝 Dev process started with PID: ${DEV_PID} (saved to ${PID_FILE})"
|
|
145
152
|
|
|
146
|
-
|
|
147
|
-
wait "${dev_pid}" || true
|
|
153
|
+
wait "${DEV_PID}"
|
|
148
154
|
}
|
|
149
155
|
|
|
150
156
|
echo "Starting HTTP services on port ${DEPLOY_RUN_PORT} (web) and ${SERVER_PORT} (server)..."
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { spawn } from 'child_process';
|
|
3
3
|
import { resolve, join, basename } from 'path';
|
|
4
4
|
import { appendFileSync, openSync, closeSync, mkdirSync } from 'fs';
|
|
5
|
+
import { homedir } from 'os';
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
|
|
@@ -81,7 +82,8 @@ const config = {
|
|
|
81
82
|
const projectRoot = resolve(outputPath);
|
|
82
83
|
|
|
83
84
|
// Determine log directory
|
|
84
|
-
const
|
|
85
|
+
const cozeHome = process.env.COZE_HOME || join(homedir(), '.coze');
|
|
86
|
+
const logDir = process.env.COZE_LOG_DIR || join(cozeHome, 'logs');
|
|
85
87
|
mkdirSync(logDir, { recursive: true });
|
|
86
88
|
|
|
87
89
|
// Use project name in log file to avoid conflicts
|