@coze-arch/cli 0.1.5 → 0.1.7
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 +12 -1
- package/lib/__templates__/expo/.cozeproj/scripts/dev_build.sh +3 -3
- package/lib/__templates__/expo/.cozeproj/scripts/dev_run.sh +11 -3
- package/lib/__templates__/expo/.cozeproj/scripts/prepare-node-modules.sh +212 -0
- package/lib/__templates__/expo/.cozeproj/scripts/prod_build.sh +3 -3
- package/lib/__templates__/expo/client/components/Screen.tsx +65 -25
- package/lib/__templates__/expo/client/heroui/components/toast/toast.tsx +6 -2
- package/lib/__templates__/expo/client/heroui/helpers/internal/hooks/use-controllable-state.ts +1 -1
- package/lib/__templates__/nextjs/eslint.config.mjs +11 -5
- package/lib/__templates__/nextjs/next.config.ts +10 -0
- package/lib/__templates__/nextjs/package.json +2 -5
- package/lib/__templates__/nextjs/pnpm-lock.yaml +14 -945
- package/lib/__templates__/nextjs/scripts/build.ps1 +1 -1
- package/lib/__templates__/nextjs/scripts/build.sh +2 -2
- package/lib/__templates__/nextjs/scripts/dev.ps1 +2 -2
- package/lib/__templates__/nextjs/scripts/dev.sh +86 -10
- package/lib/__templates__/nextjs/scripts/prepare-node-modules.sh +212 -0
- package/lib/__templates__/nextjs/scripts/prepare.sh +1 -4
- package/lib/__templates__/nextjs/src/app/layout.tsx +0 -4
- package/lib/__templates__/nextjs/src/server.ts +1 -1
- package/lib/__templates__/nuxt-vue/scripts/build.sh +1 -1
- package/lib/__templates__/nuxt-vue/scripts/dev.sh +11 -3
- package/lib/__templates__/nuxt-vue/scripts/prepare-node-modules.sh +212 -0
- package/lib/__templates__/nuxt-vue/scripts/prepare.sh +1 -4
- package/lib/__templates__/taro/.cozeproj/scripts/deploy_build.sh +5 -1
- package/lib/__templates__/taro/.cozeproj/scripts/deploy_run.sh +4 -0
- package/lib/__templates__/taro/.cozeproj/scripts/dev_build.sh +3 -0
- package/lib/__templates__/taro/.cozeproj/scripts/dev_run.ps1 +4 -2
- package/lib/__templates__/taro/.cozeproj/scripts/dev_run.sh +29 -7
- package/lib/__templates__/taro/.cozeproj/scripts/init_env.sh +4 -0
- package/lib/__templates__/taro/.cozeproj/scripts/pack.ps1 +3 -1
- package/lib/__templates__/taro/.cozeproj/scripts/pack.sh +8 -0
- package/lib/__templates__/taro/.cozeproj/scripts/prepare-node-modules.sh +212 -0
- package/lib/__templates__/taro/.cozeproj/scripts/validate.ps1 +3 -1
- package/lib/__templates__/taro/.cozeproj/scripts/validate.sh +4 -0
- package/lib/__templates__/taro/_gitignore +1 -0
- package/lib/__templates__/vite/scripts/build.sh +1 -1
- package/lib/__templates__/vite/scripts/dev.ps1 +2 -2
- package/lib/__templates__/vite/scripts/dev.sh +11 -3
- package/lib/__templates__/vite/scripts/prepare-node-modules.sh +212 -0
- package/lib/__templates__/vite/scripts/prepare.sh +1 -4
- package/lib/cli.js +147 -16
- package/package.json +2 -2
- package/lib/__templates__/nextjs/.babelrc +0 -15
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Install dependencies on local disk and expose them to the source tree by symlink.
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
|
|
6
|
+
if [ "$(basename "$(dirname "$SCRIPT_DIR")")" = ".cozeproj" ]; then
|
|
7
|
+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd -P)"
|
|
8
|
+
else
|
|
9
|
+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd -P)"
|
|
10
|
+
fi
|
|
11
|
+
|
|
12
|
+
command -v pnpm >/dev/null 2>&1 || {
|
|
13
|
+
echo "[node_modules] pnpm is required" >&2
|
|
14
|
+
exit 1
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
DRIVE_ROOT="${COZE_DRIVE_ROOT:-/Coze/Drive}"
|
|
18
|
+
if [ -d "$DRIVE_ROOT" ]; then
|
|
19
|
+
DRIVE_ROOT="$(cd "$DRIVE_ROOT" && pwd -P)"
|
|
20
|
+
else
|
|
21
|
+
DRIVE_ROOT="${DRIVE_ROOT%/}"
|
|
22
|
+
fi
|
|
23
|
+
case "$PROJECT_ROOT" in
|
|
24
|
+
"$DRIVE_ROOT"|"$DRIVE_ROOT"/*) ;;
|
|
25
|
+
*)
|
|
26
|
+
(cd "$PROJECT_ROOT" && pnpm install "$@")
|
|
27
|
+
exit 0
|
|
28
|
+
;;
|
|
29
|
+
esac
|
|
30
|
+
|
|
31
|
+
NM_ROOT="/tmp/nm"
|
|
32
|
+
PROJECT_ID="$(basename "$PROJECT_ROOT")-$(printf '%s' "$PROJECT_ROOT" | cksum | awk '{print $1}')"
|
|
33
|
+
mkdir -p "$NM_ROOT"
|
|
34
|
+
WORK_DIR="$NM_ROOT/$PROJECT_ID"
|
|
35
|
+
case "$WORK_DIR" in
|
|
36
|
+
"$DRIVE_ROOT"|"$DRIVE_ROOT"/*)
|
|
37
|
+
echo "[node_modules] local dependency directory must not be on Coze Drive: $WORK_DIR" >&2
|
|
38
|
+
exit 1
|
|
39
|
+
;;
|
|
40
|
+
esac
|
|
41
|
+
|
|
42
|
+
LOCK_DIR="$NM_ROOT/$PROJECT_ID.lock"
|
|
43
|
+
LOCK_TIMEOUT_SECONDS="${COZE_NODE_MODULES_LOCK_TIMEOUT_SECONDS:-120}"
|
|
44
|
+
case "$LOCK_TIMEOUT_SECONDS" in
|
|
45
|
+
''|*[!0-9]*|0)
|
|
46
|
+
echo "[node_modules] COZE_NODE_MODULES_LOCK_TIMEOUT_SECONDS must be a positive integer" >&2
|
|
47
|
+
exit 1
|
|
48
|
+
;;
|
|
49
|
+
esac
|
|
50
|
+
|
|
51
|
+
lock_owner_is_alive() {
|
|
52
|
+
local owner_pid=""
|
|
53
|
+
[ -f "$LOCK_DIR/pid" ] || return 1
|
|
54
|
+
IFS= read -r owner_pid < "$LOCK_DIR/pid" || return 1
|
|
55
|
+
case "$owner_pid" in
|
|
56
|
+
''|*[!0-9]*) return 1 ;;
|
|
57
|
+
esac
|
|
58
|
+
kill -0 "$owner_pid" 2>/dev/null
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
release_lock() {
|
|
62
|
+
local owner_pid=""
|
|
63
|
+
[ -f "$LOCK_DIR/pid" ] || return 0
|
|
64
|
+
IFS= read -r owner_pid < "$LOCK_DIR/pid" || true
|
|
65
|
+
if [ "$owner_pid" = "$$" ]; then
|
|
66
|
+
rm -f "$LOCK_DIR/pid"
|
|
67
|
+
rmdir "$LOCK_DIR" 2>/dev/null || true
|
|
68
|
+
fi
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
lock_started_at="$(date +%s)"
|
|
72
|
+
missing_owner_checks=0
|
|
73
|
+
while ! mkdir "$LOCK_DIR" 2>/dev/null; do
|
|
74
|
+
if lock_owner_is_alive; then
|
|
75
|
+
missing_owner_checks=0
|
|
76
|
+
else
|
|
77
|
+
missing_owner_checks=$((missing_owner_checks + 1))
|
|
78
|
+
if [ "$missing_owner_checks" -ge 2 ]; then
|
|
79
|
+
stale_lock_dir="$LOCK_DIR.stale.$$"
|
|
80
|
+
if mv "$LOCK_DIR" "$stale_lock_dir" 2>/dev/null; then
|
|
81
|
+
echo "[node_modules] Recovering stale dependency preparation lock."
|
|
82
|
+
rm -rf "$stale_lock_dir"
|
|
83
|
+
fi
|
|
84
|
+
missing_owner_checks=0
|
|
85
|
+
continue
|
|
86
|
+
fi
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
lock_now="$(date +%s)"
|
|
90
|
+
if [ $((lock_now - lock_started_at)) -ge "$LOCK_TIMEOUT_SECONDS" ]; then
|
|
91
|
+
echo "[node_modules] Timed out waiting ${LOCK_TIMEOUT_SECONDS}s for dependency preparation lock: $LOCK_DIR" >&2
|
|
92
|
+
exit 1
|
|
93
|
+
fi
|
|
94
|
+
echo "[node_modules] Waiting for another dependency preparation for this project..."
|
|
95
|
+
sleep 1
|
|
96
|
+
done
|
|
97
|
+
printf '%s\n' "$$" > "$LOCK_DIR/pid"
|
|
98
|
+
trap release_lock EXIT
|
|
99
|
+
|
|
100
|
+
mkdir -p "$WORK_DIR"
|
|
101
|
+
|
|
102
|
+
# Previous versions mirrored the whole project into WORK_DIR. That gives pnpm
|
|
103
|
+
# and framework dev servers two paths for the same source tree, which breaks
|
|
104
|
+
# file watching and on-demand compilation. Keep only managed local outputs.
|
|
105
|
+
cleanup_legacy_workspace() {
|
|
106
|
+
local workspace_dir="$1" entry name
|
|
107
|
+
if [ -L "$workspace_dir" ] || { [ -e "$workspace_dir" ] && [ ! -d "$workspace_dir" ]; }; then
|
|
108
|
+
rm -rf "$workspace_dir"
|
|
109
|
+
return
|
|
110
|
+
fi
|
|
111
|
+
if [ ! -d "$workspace_dir" ]; then
|
|
112
|
+
return 0
|
|
113
|
+
fi
|
|
114
|
+
|
|
115
|
+
while IFS= read -r -d '' entry; do
|
|
116
|
+
name="$(basename "$entry")"
|
|
117
|
+
[ "$name" = "node_modules" ] || rm -rf "$entry"
|
|
118
|
+
done < <(find "$workspace_dir" -mindepth 1 -maxdepth 1 -print0)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
cleanup_legacy_shadow_project() {
|
|
122
|
+
local entry name
|
|
123
|
+
while IFS= read -r -d '' entry; do
|
|
124
|
+
name="$(basename "$entry")"
|
|
125
|
+
case "$name" in
|
|
126
|
+
node_modules|client|server) continue ;;
|
|
127
|
+
esac
|
|
128
|
+
rm -rf "$entry"
|
|
129
|
+
done < <(find "$WORK_DIR" -mindepth 1 -maxdepth 1 -print0)
|
|
130
|
+
|
|
131
|
+
for workspace in client server; do
|
|
132
|
+
cleanup_legacy_workspace "$WORK_DIR/$workspace"
|
|
133
|
+
done
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
link_install_entry() {
|
|
137
|
+
local source_path="$1" target_path="$2"
|
|
138
|
+
rm -rf "$target_path"
|
|
139
|
+
if [ -e "$source_path" ]; then
|
|
140
|
+
ln -s "$source_path" "$target_path"
|
|
141
|
+
fi
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
copy_install_metadata() {
|
|
145
|
+
local filename source_file target_file workspace
|
|
146
|
+
for filename in pnpm-lock.yaml pnpm-workspace.yaml .npmrc; do
|
|
147
|
+
source_file="$PROJECT_ROOT/$filename"
|
|
148
|
+
target_file="$WORK_DIR/$filename"
|
|
149
|
+
rm -f "$target_file"
|
|
150
|
+
if [ -f "$source_file" ]; then
|
|
151
|
+
cp "$source_file" "$target_file"
|
|
152
|
+
fi
|
|
153
|
+
done
|
|
154
|
+
|
|
155
|
+
link_install_entry "$PROJECT_ROOT/package.json" "$WORK_DIR/package.json"
|
|
156
|
+
link_install_entry "$PROJECT_ROOT/patches" "$WORK_DIR/patches"
|
|
157
|
+
link_install_entry "$PROJECT_ROOT/scripts" "$WORK_DIR/scripts"
|
|
158
|
+
|
|
159
|
+
for workspace in client server; do
|
|
160
|
+
if [ -f "$PROJECT_ROOT/$workspace/package.json" ]; then
|
|
161
|
+
mkdir -p "$WORK_DIR/$workspace"
|
|
162
|
+
link_install_entry "$PROJECT_ROOT/$workspace/package.json" "$WORK_DIR/$workspace/package.json"
|
|
163
|
+
link_install_entry "$PROJECT_ROOT/$workspace/scripts" "$WORK_DIR/$workspace/scripts"
|
|
164
|
+
else
|
|
165
|
+
rm -rf "$WORK_DIR/$workspace"
|
|
166
|
+
fi
|
|
167
|
+
done
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
link_node_modules() {
|
|
171
|
+
local source_dir="$1" target_dir="$2"
|
|
172
|
+
local current=""
|
|
173
|
+
mkdir -p "$target_dir"
|
|
174
|
+
if [ -L "$source_dir/node_modules" ]; then
|
|
175
|
+
current="$(readlink "$source_dir/node_modules")"
|
|
176
|
+
case "$current" in
|
|
177
|
+
"$NM_ROOT"/*/node_modules|"$NM_ROOT"/*/*/node_modules) ;;
|
|
178
|
+
*)
|
|
179
|
+
echo "[node_modules] refusing to replace unmanaged symlink: $source_dir/node_modules" >&2
|
|
180
|
+
exit 1
|
|
181
|
+
;;
|
|
182
|
+
esac
|
|
183
|
+
rm "$source_dir/node_modules"
|
|
184
|
+
elif [ -e "$source_dir/node_modules" ]; then
|
|
185
|
+
rm -rf "$source_dir/node_modules"
|
|
186
|
+
fi
|
|
187
|
+
ln -s "$target_dir" "$source_dir/node_modules"
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
cleanup_legacy_shadow_project
|
|
191
|
+
copy_install_metadata
|
|
192
|
+
|
|
193
|
+
echo "[node_modules] Installing dependencies in $WORK_DIR"
|
|
194
|
+
if ! (cd "$WORK_DIR" && pnpm install "$@"); then
|
|
195
|
+
exit 1
|
|
196
|
+
fi
|
|
197
|
+
|
|
198
|
+
if [ -f "$WORK_DIR/pnpm-lock.yaml" ]; then
|
|
199
|
+
temporary_lockfile="$(mktemp "$PROJECT_ROOT/.pnpm-lock.yaml.XXXXXX")"
|
|
200
|
+
cp "$WORK_DIR/pnpm-lock.yaml" "$temporary_lockfile"
|
|
201
|
+
chmod 0644 "$temporary_lockfile"
|
|
202
|
+
mv -f "$temporary_lockfile" "$PROJECT_ROOT/pnpm-lock.yaml"
|
|
203
|
+
fi
|
|
204
|
+
|
|
205
|
+
link_node_modules "$PROJECT_ROOT" "$WORK_DIR/node_modules"
|
|
206
|
+
for workspace in client server; do
|
|
207
|
+
if [ -f "$PROJECT_ROOT/$workspace/package.json" ]; then
|
|
208
|
+
link_node_modules "$PROJECT_ROOT/$workspace" "$WORK_DIR/$workspace/node_modules"
|
|
209
|
+
fi
|
|
210
|
+
done
|
|
211
|
+
|
|
212
|
+
echo "[node_modules] Dependencies ready"
|
|
@@ -6,10 +6,7 @@ COZE_WORKSPACE_PATH="${COZE_WORKSPACE_PATH:-$(pwd)}"
|
|
|
6
6
|
cd "${COZE_WORKSPACE_PATH}"
|
|
7
7
|
|
|
8
8
|
echo "Installing dependencies..."
|
|
9
|
-
|
|
10
|
-
if command -v coze-dev > /dev/null 2>&1 && coze-dev check-bins --help > /dev/null 2>&1; then
|
|
11
|
-
coze-dev check-bins --fix
|
|
12
|
-
fi
|
|
9
|
+
bash "$COZE_WORKSPACE_PATH/scripts/prepare-node-modules.sh" --prefer-frozen-lockfile --prefer-offline
|
|
13
10
|
|
|
14
11
|
echo "Preparing Nuxt project..."
|
|
15
12
|
pnpm exec nuxt prepare
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
set -Eeuo pipefail
|
|
3
3
|
|
|
4
|
+
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
5
|
+
COZE_WORKSPACE_PATH="${COZE_WORKSPACE_PATH:-${ROOT_DIR}}"
|
|
6
|
+
export COZE_WORKSPACE_PATH
|
|
7
|
+
|
|
4
8
|
cd "${COZE_WORKSPACE_PATH}"
|
|
5
9
|
if [ -f "./.cozeproj/scripts/init_env.sh" ]; then
|
|
6
10
|
echo "⚙️ Initializing environment..."
|
|
@@ -11,7 +15,7 @@ else
|
|
|
11
15
|
fi
|
|
12
16
|
echo "Installing dependencies..."
|
|
13
17
|
# 安装所有依赖(包含 Taro 核心和 React)
|
|
14
|
-
|
|
18
|
+
bash "$ROOT_DIR/.cozeproj/scripts/prepare-node-modules.sh" --prefer-frozen-lockfile --prefer-offline
|
|
15
19
|
|
|
16
20
|
echo "Building the Taro project..."
|
|
17
21
|
pnpm build
|
|
@@ -28,7 +28,9 @@ function Start-DevProcess([string] $workingDirectory, [string] $logFile, [hashta
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
$
|
|
31
|
+
$projectRoot = (Resolve-Path (Join-Path $PSScriptRoot "../..")).Path
|
|
32
|
+
$workspace = if ($env:COZE_WORKSPACE_PATH) { $env:COZE_WORKSPACE_PATH } else { $projectRoot }
|
|
33
|
+
$env:COZE_WORKSPACE_PATH = $workspace
|
|
32
34
|
$port = if ($env:DEPLOY_RUN_PORT) { [int] $env:DEPLOY_RUN_PORT } elseif ($env:PORT) { [int] $env:PORT } else { <%= port %> }
|
|
33
35
|
$serverPort = if ($env:SERVER_PORT) { [int] $env:SERVER_PORT } else { <%= serverPort %> }
|
|
34
36
|
$logDirectory = if ($env:COZE_LOG_DIR) { $env:COZE_LOG_DIR } else { Join-Path $workspace "logs" }
|
|
@@ -43,7 +45,7 @@ if (Test-Path $pidFile) {
|
|
|
43
45
|
Stop-ListeningProcess $port
|
|
44
46
|
Stop-ListeningProcess $serverPort
|
|
45
47
|
|
|
46
|
-
$environment = @{ PORT = "$port"; SERVER_PORT = "$serverPort" }
|
|
48
|
+
$environment = @{ COZE_WORKSPACE_PATH = $workspace; PORT = "$port"; SERVER_PORT = "$serverPort" }
|
|
47
49
|
if ($env:COZE_PROJECT_DOMAIN_DEFAULT) {
|
|
48
50
|
$environment.PROJECT_DOMAIN = $env:COZE_PROJECT_DOMAIN_DEFAULT
|
|
49
51
|
}
|
|
@@ -4,6 +4,7 @@ set -Eeuo pipefail
|
|
|
4
4
|
|
|
5
5
|
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
6
6
|
COZE_WORKSPACE_PATH="${COZE_WORKSPACE_PATH:-${ROOT_DIR}}"
|
|
7
|
+
export COZE_WORKSPACE_PATH
|
|
7
8
|
cd "${COZE_WORKSPACE_PATH}"
|
|
8
9
|
|
|
9
10
|
# ---------------------------------------------------------
|
|
@@ -31,11 +32,15 @@ kill_process_tree() {
|
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
# detached 出去的进程没人负责回收,超过这个时长就自己退出,避免端口与内存长期泄露。
|
|
34
|
-
MAX_RUNTIME_SECONDS=
|
|
35
|
+
MAX_RUNTIME_SECONDS=3600
|
|
36
|
+
|
|
37
|
+
timeout_watchdog_enabled() {
|
|
38
|
+
[[ -z "${COZE_EVAL:-}" && -z "${COZE_PROJECT_TYPE:-}" ]]
|
|
39
|
+
}
|
|
35
40
|
|
|
36
41
|
# 真正被 detach 的是这层 bash wrapper:它是进程组 leader,组内 watchdog 到点回收整组
|
|
37
42
|
# (wrapper -> pnpm -> taro/nest);被包的进程自己先退出时也顺手清空进程组,不留残余。
|
|
38
|
-
RUN_WITH_TIMEOUT='
|
|
43
|
+
RUN_WITH_TIMEOUT="$(declare -f timeout_watchdog_enabled)"'
|
|
39
44
|
timeout_seconds=$1
|
|
40
45
|
shift
|
|
41
46
|
|
|
@@ -43,6 +48,7 @@ shift
|
|
|
43
48
|
child_pid=$!
|
|
44
49
|
|
|
45
50
|
# 先忽略 TERM,才能在向整组发 TERM(自己也在组里)之后存活下来补一发 KILL。
|
|
51
|
+
if timeout_watchdog_enabled; then
|
|
46
52
|
( trap "" TERM
|
|
47
53
|
sleep "${timeout_seconds}"
|
|
48
54
|
echo "[dev] 后台进程运行超过 ${timeout_seconds}s,回收进程组 $$。"
|
|
@@ -50,6 +56,7 @@ child_pid=$!
|
|
|
50
56
|
sleep 5
|
|
51
57
|
kill -KILL -- "-$$" 2>/dev/null || true
|
|
52
58
|
) &
|
|
59
|
+
fi
|
|
53
60
|
|
|
54
61
|
wait "${child_pid}"
|
|
55
62
|
kill -KILL -- "-$$" 2>/dev/null || true
|
|
@@ -155,12 +162,24 @@ list_port_pids() {
|
|
|
155
162
|
|
|
156
163
|
kill_port_if_listening() {
|
|
157
164
|
local port=$1
|
|
165
|
+
# 用于错误提示的端口环境变量名称,例如 DEPLOY_RUN_PORT 或 SERVER_PORT。
|
|
166
|
+
local port_env_name=$2
|
|
158
167
|
local pids
|
|
168
|
+
local pid
|
|
159
169
|
pids=$(list_port_pids "${port}")
|
|
160
170
|
if [[ -z "${pids}" ]]; then
|
|
161
171
|
echo "Port ${port} is free."
|
|
162
172
|
return
|
|
163
173
|
fi
|
|
174
|
+
|
|
175
|
+
# 端口可能由另一个项目占用。必须在杀进程前校验全部 PID,避免清理到一半才发现外部进程。
|
|
176
|
+
for pid in ${pids}; do
|
|
177
|
+
if ! process_belongs_to_workspace "${pid}"; then
|
|
178
|
+
echo "Error: port ${port} is occupied by PID ${pid}, which does not belong to workspace ${COZE_WORKSPACE_PATH}. Refusing to kill it. Set ${port_env_name} to another port and retry." >&2
|
|
179
|
+
return 1
|
|
180
|
+
fi
|
|
181
|
+
done
|
|
182
|
+
|
|
164
183
|
echo "Port ${port} in use by PIDs: ${pids} (SIGKILL)"
|
|
165
184
|
for pid in ${pids}; do
|
|
166
185
|
kill_process_tree "${pid}"
|
|
@@ -168,7 +187,8 @@ kill_port_if_listening() {
|
|
|
168
187
|
sleep 1
|
|
169
188
|
pids=$(list_port_pids "${port}")
|
|
170
189
|
if [[ -n "${pids}" ]]; then
|
|
171
|
-
echo "
|
|
190
|
+
echo "Error: port ${port} is still occupied after cleaning up this project's processes, PIDs: ${pids}. Check the remaining processes manually or set ${port_env_name} to another port and retry." >&2
|
|
191
|
+
return 1
|
|
172
192
|
else
|
|
173
193
|
echo "Port ${port} cleared."
|
|
174
194
|
fi
|
|
@@ -194,7 +214,7 @@ cleanup_previous_run() {
|
|
|
194
214
|
# ---------------------------------------------------------
|
|
195
215
|
echo "📦 Installing dependencies..."
|
|
196
216
|
PNPM_BIN="$(command -v pnpm)"
|
|
197
|
-
"$
|
|
217
|
+
bash "$ROOT_DIR/.cozeproj/scripts/prepare-node-modules.sh" --prefer-frozen-lockfile --prefer-offline
|
|
198
218
|
echo "✅ Dependencies installed successfully!"
|
|
199
219
|
|
|
200
220
|
# ---------------------------------------------------------
|
|
@@ -210,9 +230,9 @@ mkdir -p "${LOG_DIR}"
|
|
|
210
230
|
cleanup_previous_run
|
|
211
231
|
|
|
212
232
|
echo "Clearing port ${PORT} (web) before start."
|
|
213
|
-
kill_port_if_listening "${PORT}"
|
|
233
|
+
kill_port_if_listening "${PORT}" "DEPLOY_RUN_PORT"
|
|
214
234
|
echo "Clearing port ${SERVER_PORT} (server) before start."
|
|
215
|
-
kill_port_if_listening "${SERVER_PORT}"
|
|
235
|
+
kill_port_if_listening "${SERVER_PORT}" "SERVER_PORT"
|
|
216
236
|
|
|
217
237
|
# ---------------------------------------------------------
|
|
218
238
|
# 4. 启动服务
|
|
@@ -252,7 +272,9 @@ start_service() {
|
|
|
252
272
|
fi
|
|
253
273
|
|
|
254
274
|
echo "📝 Dev process started with PID: ${DEV_PID}"
|
|
255
|
-
|
|
275
|
+
if timeout_watchdog_enabled; then
|
|
276
|
+
echo "Auto stop after ${MAX_RUNTIME_SECONDS}s."
|
|
277
|
+
fi
|
|
256
278
|
echo "Log file: ${LOG_FILE}"
|
|
257
279
|
echo "PID file: ${PID_FILE}"
|
|
258
280
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
$ErrorActionPreference = "Stop"
|
|
2
2
|
|
|
3
|
-
$
|
|
3
|
+
$projectRoot = (Resolve-Path (Join-Path $PSScriptRoot "../..")).Path
|
|
4
|
+
$workspace = if ($env:COZE_WORKSPACE_PATH) { $env:COZE_WORKSPACE_PATH } else { $projectRoot }
|
|
5
|
+
$env:COZE_WORKSPACE_PATH = $workspace
|
|
4
6
|
Set-Location $workspace
|
|
5
7
|
|
|
6
8
|
& pnpm build:pack
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
4
|
+
COZE_WORKSPACE_PATH="${COZE_WORKSPACE_PATH:-${ROOT_DIR}}"
|
|
5
|
+
export COZE_WORKSPACE_PATH
|
|
6
|
+
|
|
7
|
+
cd "${COZE_WORKSPACE_PATH}"
|
|
8
|
+
|
|
1
9
|
# build_weapp.sh - 通过 PID 文件精确杀掉自己上次的构建进程
|
|
2
10
|
PID_FILE="/tmp/coze-build_weapp.pid"
|
|
3
11
|
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Install dependencies on local disk and expose them to the source tree by symlink.
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
|
|
6
|
+
if [ "$(basename "$(dirname "$SCRIPT_DIR")")" = ".cozeproj" ]; then
|
|
7
|
+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd -P)"
|
|
8
|
+
else
|
|
9
|
+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd -P)"
|
|
10
|
+
fi
|
|
11
|
+
|
|
12
|
+
command -v pnpm >/dev/null 2>&1 || {
|
|
13
|
+
echo "[node_modules] pnpm is required" >&2
|
|
14
|
+
exit 1
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
DRIVE_ROOT="${COZE_DRIVE_ROOT:-/Coze/Drive}"
|
|
18
|
+
if [ -d "$DRIVE_ROOT" ]; then
|
|
19
|
+
DRIVE_ROOT="$(cd "$DRIVE_ROOT" && pwd -P)"
|
|
20
|
+
else
|
|
21
|
+
DRIVE_ROOT="${DRIVE_ROOT%/}"
|
|
22
|
+
fi
|
|
23
|
+
case "$PROJECT_ROOT" in
|
|
24
|
+
"$DRIVE_ROOT"|"$DRIVE_ROOT"/*) ;;
|
|
25
|
+
*)
|
|
26
|
+
(cd "$PROJECT_ROOT" && pnpm install "$@")
|
|
27
|
+
exit 0
|
|
28
|
+
;;
|
|
29
|
+
esac
|
|
30
|
+
|
|
31
|
+
NM_ROOT="/tmp/nm"
|
|
32
|
+
PROJECT_ID="$(basename "$PROJECT_ROOT")-$(printf '%s' "$PROJECT_ROOT" | cksum | awk '{print $1}')"
|
|
33
|
+
mkdir -p "$NM_ROOT"
|
|
34
|
+
WORK_DIR="$NM_ROOT/$PROJECT_ID"
|
|
35
|
+
case "$WORK_DIR" in
|
|
36
|
+
"$DRIVE_ROOT"|"$DRIVE_ROOT"/*)
|
|
37
|
+
echo "[node_modules] local dependency directory must not be on Coze Drive: $WORK_DIR" >&2
|
|
38
|
+
exit 1
|
|
39
|
+
;;
|
|
40
|
+
esac
|
|
41
|
+
|
|
42
|
+
LOCK_DIR="$NM_ROOT/$PROJECT_ID.lock"
|
|
43
|
+
LOCK_TIMEOUT_SECONDS="${COZE_NODE_MODULES_LOCK_TIMEOUT_SECONDS:-120}"
|
|
44
|
+
case "$LOCK_TIMEOUT_SECONDS" in
|
|
45
|
+
''|*[!0-9]*|0)
|
|
46
|
+
echo "[node_modules] COZE_NODE_MODULES_LOCK_TIMEOUT_SECONDS must be a positive integer" >&2
|
|
47
|
+
exit 1
|
|
48
|
+
;;
|
|
49
|
+
esac
|
|
50
|
+
|
|
51
|
+
lock_owner_is_alive() {
|
|
52
|
+
local owner_pid=""
|
|
53
|
+
[ -f "$LOCK_DIR/pid" ] || return 1
|
|
54
|
+
IFS= read -r owner_pid < "$LOCK_DIR/pid" || return 1
|
|
55
|
+
case "$owner_pid" in
|
|
56
|
+
''|*[!0-9]*) return 1 ;;
|
|
57
|
+
esac
|
|
58
|
+
kill -0 "$owner_pid" 2>/dev/null
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
release_lock() {
|
|
62
|
+
local owner_pid=""
|
|
63
|
+
[ -f "$LOCK_DIR/pid" ] || return 0
|
|
64
|
+
IFS= read -r owner_pid < "$LOCK_DIR/pid" || true
|
|
65
|
+
if [ "$owner_pid" = "$$" ]; then
|
|
66
|
+
rm -f "$LOCK_DIR/pid"
|
|
67
|
+
rmdir "$LOCK_DIR" 2>/dev/null || true
|
|
68
|
+
fi
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
lock_started_at="$(date +%s)"
|
|
72
|
+
missing_owner_checks=0
|
|
73
|
+
while ! mkdir "$LOCK_DIR" 2>/dev/null; do
|
|
74
|
+
if lock_owner_is_alive; then
|
|
75
|
+
missing_owner_checks=0
|
|
76
|
+
else
|
|
77
|
+
missing_owner_checks=$((missing_owner_checks + 1))
|
|
78
|
+
if [ "$missing_owner_checks" -ge 2 ]; then
|
|
79
|
+
stale_lock_dir="$LOCK_DIR.stale.$$"
|
|
80
|
+
if mv "$LOCK_DIR" "$stale_lock_dir" 2>/dev/null; then
|
|
81
|
+
echo "[node_modules] Recovering stale dependency preparation lock."
|
|
82
|
+
rm -rf "$stale_lock_dir"
|
|
83
|
+
fi
|
|
84
|
+
missing_owner_checks=0
|
|
85
|
+
continue
|
|
86
|
+
fi
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
lock_now="$(date +%s)"
|
|
90
|
+
if [ $((lock_now - lock_started_at)) -ge "$LOCK_TIMEOUT_SECONDS" ]; then
|
|
91
|
+
echo "[node_modules] Timed out waiting ${LOCK_TIMEOUT_SECONDS}s for dependency preparation lock: $LOCK_DIR" >&2
|
|
92
|
+
exit 1
|
|
93
|
+
fi
|
|
94
|
+
echo "[node_modules] Waiting for another dependency preparation for this project..."
|
|
95
|
+
sleep 1
|
|
96
|
+
done
|
|
97
|
+
printf '%s\n' "$$" > "$LOCK_DIR/pid"
|
|
98
|
+
trap release_lock EXIT
|
|
99
|
+
|
|
100
|
+
mkdir -p "$WORK_DIR"
|
|
101
|
+
|
|
102
|
+
# Previous versions mirrored the whole project into WORK_DIR. That gives pnpm
|
|
103
|
+
# and framework dev servers two paths for the same source tree, which breaks
|
|
104
|
+
# file watching and on-demand compilation. Keep only managed local outputs.
|
|
105
|
+
cleanup_legacy_workspace() {
|
|
106
|
+
local workspace_dir="$1" entry name
|
|
107
|
+
if [ -L "$workspace_dir" ] || { [ -e "$workspace_dir" ] && [ ! -d "$workspace_dir" ]; }; then
|
|
108
|
+
rm -rf "$workspace_dir"
|
|
109
|
+
return
|
|
110
|
+
fi
|
|
111
|
+
if [ ! -d "$workspace_dir" ]; then
|
|
112
|
+
return 0
|
|
113
|
+
fi
|
|
114
|
+
|
|
115
|
+
while IFS= read -r -d '' entry; do
|
|
116
|
+
name="$(basename "$entry")"
|
|
117
|
+
[ "$name" = "node_modules" ] || rm -rf "$entry"
|
|
118
|
+
done < <(find "$workspace_dir" -mindepth 1 -maxdepth 1 -print0)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
cleanup_legacy_shadow_project() {
|
|
122
|
+
local entry name
|
|
123
|
+
while IFS= read -r -d '' entry; do
|
|
124
|
+
name="$(basename "$entry")"
|
|
125
|
+
case "$name" in
|
|
126
|
+
node_modules|client|server) continue ;;
|
|
127
|
+
esac
|
|
128
|
+
rm -rf "$entry"
|
|
129
|
+
done < <(find "$WORK_DIR" -mindepth 1 -maxdepth 1 -print0)
|
|
130
|
+
|
|
131
|
+
for workspace in client server; do
|
|
132
|
+
cleanup_legacy_workspace "$WORK_DIR/$workspace"
|
|
133
|
+
done
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
link_install_entry() {
|
|
137
|
+
local source_path="$1" target_path="$2"
|
|
138
|
+
rm -rf "$target_path"
|
|
139
|
+
if [ -e "$source_path" ]; then
|
|
140
|
+
ln -s "$source_path" "$target_path"
|
|
141
|
+
fi
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
copy_install_metadata() {
|
|
145
|
+
local filename source_file target_file workspace
|
|
146
|
+
for filename in pnpm-lock.yaml pnpm-workspace.yaml .npmrc; do
|
|
147
|
+
source_file="$PROJECT_ROOT/$filename"
|
|
148
|
+
target_file="$WORK_DIR/$filename"
|
|
149
|
+
rm -f "$target_file"
|
|
150
|
+
if [ -f "$source_file" ]; then
|
|
151
|
+
cp "$source_file" "$target_file"
|
|
152
|
+
fi
|
|
153
|
+
done
|
|
154
|
+
|
|
155
|
+
link_install_entry "$PROJECT_ROOT/package.json" "$WORK_DIR/package.json"
|
|
156
|
+
link_install_entry "$PROJECT_ROOT/patches" "$WORK_DIR/patches"
|
|
157
|
+
link_install_entry "$PROJECT_ROOT/scripts" "$WORK_DIR/scripts"
|
|
158
|
+
|
|
159
|
+
for workspace in client server; do
|
|
160
|
+
if [ -f "$PROJECT_ROOT/$workspace/package.json" ]; then
|
|
161
|
+
mkdir -p "$WORK_DIR/$workspace"
|
|
162
|
+
link_install_entry "$PROJECT_ROOT/$workspace/package.json" "$WORK_DIR/$workspace/package.json"
|
|
163
|
+
link_install_entry "$PROJECT_ROOT/$workspace/scripts" "$WORK_DIR/$workspace/scripts"
|
|
164
|
+
else
|
|
165
|
+
rm -rf "$WORK_DIR/$workspace"
|
|
166
|
+
fi
|
|
167
|
+
done
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
link_node_modules() {
|
|
171
|
+
local source_dir="$1" target_dir="$2"
|
|
172
|
+
local current=""
|
|
173
|
+
mkdir -p "$target_dir"
|
|
174
|
+
if [ -L "$source_dir/node_modules" ]; then
|
|
175
|
+
current="$(readlink "$source_dir/node_modules")"
|
|
176
|
+
case "$current" in
|
|
177
|
+
"$NM_ROOT"/*/node_modules|"$NM_ROOT"/*/*/node_modules) ;;
|
|
178
|
+
*)
|
|
179
|
+
echo "[node_modules] refusing to replace unmanaged symlink: $source_dir/node_modules" >&2
|
|
180
|
+
exit 1
|
|
181
|
+
;;
|
|
182
|
+
esac
|
|
183
|
+
rm "$source_dir/node_modules"
|
|
184
|
+
elif [ -e "$source_dir/node_modules" ]; then
|
|
185
|
+
rm -rf "$source_dir/node_modules"
|
|
186
|
+
fi
|
|
187
|
+
ln -s "$target_dir" "$source_dir/node_modules"
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
cleanup_legacy_shadow_project
|
|
191
|
+
copy_install_metadata
|
|
192
|
+
|
|
193
|
+
echo "[node_modules] Installing dependencies in $WORK_DIR"
|
|
194
|
+
if ! (cd "$WORK_DIR" && pnpm install "$@"); then
|
|
195
|
+
exit 1
|
|
196
|
+
fi
|
|
197
|
+
|
|
198
|
+
if [ -f "$WORK_DIR/pnpm-lock.yaml" ]; then
|
|
199
|
+
temporary_lockfile="$(mktemp "$PROJECT_ROOT/.pnpm-lock.yaml.XXXXXX")"
|
|
200
|
+
cp "$WORK_DIR/pnpm-lock.yaml" "$temporary_lockfile"
|
|
201
|
+
chmod 0644 "$temporary_lockfile"
|
|
202
|
+
mv -f "$temporary_lockfile" "$PROJECT_ROOT/pnpm-lock.yaml"
|
|
203
|
+
fi
|
|
204
|
+
|
|
205
|
+
link_node_modules "$PROJECT_ROOT" "$WORK_DIR/node_modules"
|
|
206
|
+
for workspace in client server; do
|
|
207
|
+
if [ -f "$PROJECT_ROOT/$workspace/package.json" ]; then
|
|
208
|
+
link_node_modules "$PROJECT_ROOT/$workspace" "$WORK_DIR/$workspace/node_modules"
|
|
209
|
+
fi
|
|
210
|
+
done
|
|
211
|
+
|
|
212
|
+
echo "[node_modules] Dependencies ready"
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
$ErrorActionPreference = "Stop"
|
|
2
2
|
|
|
3
|
-
$
|
|
3
|
+
$projectRoot = (Resolve-Path (Join-Path $PSScriptRoot "../..")).Path
|
|
4
|
+
$workspace = if ($env:COZE_WORKSPACE_PATH) { $env:COZE_WORKSPACE_PATH } else { $projectRoot }
|
|
5
|
+
$env:COZE_WORKSPACE_PATH = $workspace
|
|
4
6
|
Set-Location $workspace
|
|
5
7
|
|
|
6
8
|
& pnpm validate
|
|
@@ -6,7 +6,7 @@ COZE_WORKSPACE_PATH="${COZE_WORKSPACE_PATH:-$(pwd)}"
|
|
|
6
6
|
cd "${COZE_WORKSPACE_PATH}"
|
|
7
7
|
|
|
8
8
|
echo "Installing dependencies..."
|
|
9
|
-
|
|
9
|
+
bash "$COZE_WORKSPACE_PATH/scripts/prepare-node-modules.sh" --prefer-frozen-lockfile --prefer-offline --loglevel debug --reporter=append-only
|
|
10
10
|
|
|
11
11
|
echo "Building frontend with Vite..."
|
|
12
12
|
pnpm vite build
|