@coze-arch/cli 0.1.8-alpha.a5ed27 → 0.1.8-alpha.a87968

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.
@@ -98,6 +98,7 @@ printf '%s\n' "$$" > "$LOCK_DIR/pid"
98
98
  trap release_lock EXIT
99
99
 
100
100
  mkdir -p "$WORK_DIR"
101
+ INSTALL_STATE_FILE="$WORK_DIR/.install-state"
101
102
 
102
103
  # Previous versions mirrored the whole project into WORK_DIR. That gives pnpm
103
104
  # and framework dev servers two paths for the same source tree, which breaks
@@ -118,12 +119,15 @@ cleanup_legacy_workspace() {
118
119
  done < <(find "$workspace_dir" -mindepth 1 -maxdepth 1 -print0)
119
120
  }
120
121
 
122
+ # caches is reserved for the sibling prepare scripts of this project. They keep
123
+ # regenerable framework output there (the Next helper points .next/dev into it), and that
124
+ # output has to outlive an install, so this cleanup must not read it as leftover.
121
125
  cleanup_legacy_shadow_project() {
122
126
  local entry name
123
127
  while IFS= read -r -d '' entry; do
124
128
  name="$(basename "$entry")"
125
129
  case "$name" in
126
- node_modules|client|server) continue ;;
130
+ node_modules|client|server|caches|.install-state) continue ;;
127
131
  esac
128
132
  rm -rf "$entry"
129
133
  done < <(find "$WORK_DIR" -mindepth 1 -maxdepth 1 -print0)
@@ -141,6 +145,162 @@ link_install_entry() {
141
145
  fi
142
146
  }
143
147
 
148
+ hash_install_input() {
149
+ if command -v sha256sum >/dev/null 2>&1; then
150
+ sha256sum | awk '{print $1}'
151
+ elif command -v shasum >/dev/null 2>&1; then
152
+ shasum -a 256 | awk '{print $1}'
153
+ else
154
+ cksum | awk '{print $1 "-" $2}'
155
+ fi
156
+ }
157
+
158
+ append_file_to_install_fingerprint() {
159
+ local relative_path="$1"
160
+ local source_path="$PROJECT_ROOT/$relative_path"
161
+ printf 'path=%s\n' "$relative_path"
162
+ if [ -f "$source_path" ]; then
163
+ cat "$source_path"
164
+ else
165
+ printf '<missing>\n'
166
+ fi
167
+ }
168
+
169
+ append_directory_to_install_fingerprint() {
170
+ local relative_path="$1"
171
+ local source_path="$PROJECT_ROOT/$relative_path"
172
+ local file
173
+ printf 'path=%s\n' "$relative_path"
174
+ if [ ! -d "$source_path" ]; then
175
+ printf '<missing>\n'
176
+ return
177
+ fi
178
+
179
+ while IFS= read -r file; do
180
+ printf 'file=%s\n' "${file#"${PROJECT_ROOT}/"}"
181
+ cat "$file"
182
+ done < <(find -P "$source_path" -type f -print | LC_ALL=C sort)
183
+ }
184
+
185
+ # Everything that can change the resolved dependency tree. The leading version marker
186
+ # invalidates every stored state when this scheme itself changes.
187
+ calculate_install_fingerprint() {
188
+ local filename workspace argument
189
+ {
190
+ printf 'prepare-node-modules-install-state=1\n'
191
+ printf 'node-version=%s\n' "$(node --version)"
192
+ printf 'pnpm-version=%s\n' "$(pnpm --version)"
193
+ printf 'node-env=%s\n' "${NODE_ENV:-}"
194
+ printf 'npm-config-production=%s\n' "${npm_config_production:-}"
195
+ printf 'npm-config-production-upper=%s\n' "${NPM_CONFIG_PRODUCTION:-}"
196
+ printf 'pnpm-config-production=%s\n' "${PNPM_CONFIG_PRODUCTION:-}"
197
+ for argument in "$@"; do
198
+ printf 'install-argument=%s\n' "$argument"
199
+ done
200
+ for filename in package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc; do
201
+ append_file_to_install_fingerprint "$filename"
202
+ done
203
+ for workspace in client server; do
204
+ append_file_to_install_fingerprint "$workspace/package.json"
205
+ done
206
+ append_directory_to_install_fingerprint patches
207
+ } | hash_install_input
208
+ }
209
+
210
+ install_requires_refresh() {
211
+ local argument
212
+ for argument in "$@"; do
213
+ case "$argument" in
214
+ --force|-f|--lockfile-only|--fix-lockfile) return 0 ;;
215
+ esac
216
+ done
217
+ return 1
218
+ }
219
+
220
+ # Reuse skips pnpm install entirely, so it must not skip side effects that live outside
221
+ # node_modules. preinstall is exempt: it only guards which package manager may run, and
222
+ # there is no install to guard when the tree is already in place.
223
+ package_has_install_lifecycle() {
224
+ local package_path="$1"
225
+ node - "$package_path" <<'NODE'
226
+ const fs = require('node:fs');
227
+ const [packagePath] = process.argv.slice(2);
228
+
229
+ try {
230
+ const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
231
+ const scripts = packageJson && typeof packageJson.scripts === 'object' && packageJson.scripts
232
+ ? packageJson.scripts
233
+ : {};
234
+ const hasLifecycle = ['install', 'postinstall', 'prepare'].some(
235
+ name => typeof scripts[name] === 'string' && scripts[name].trim() !== '',
236
+ );
237
+
238
+ process.exit(hasLifecycle ? 0 : 1);
239
+ } catch {
240
+ process.exit(0);
241
+ }
242
+ NODE
243
+ }
244
+
245
+ install_reuse_is_safe() {
246
+ local workspace package_path
247
+ for package_path in "$PROJECT_ROOT/.pnpmfile.cjs" "$PROJECT_ROOT/.pnpmfile.js"; do
248
+ if [ -e "$package_path" ]; then
249
+ echo "[node_modules] Reinstalling because pnpm hooks are present: $package_path"
250
+ return 1
251
+ fi
252
+ done
253
+
254
+ if [ -f "$PROJECT_ROOT/.npmrc" ] && grep -Eq '^[[:space:]]*pnpmfile[[:space:]]*=' "$PROJECT_ROOT/.npmrc"; then
255
+ echo "[node_modules] Reinstalling because .npmrc configures a pnpm hook."
256
+ return 1
257
+ fi
258
+
259
+ for workspace in . client server; do
260
+ package_path="$PROJECT_ROOT/$workspace/package.json"
261
+ [ -f "$package_path" ] || continue
262
+ if package_has_install_lifecycle "$package_path"; then
263
+ echo "[node_modules] Reinstalling because $package_path has an install lifecycle script."
264
+ return 1
265
+ fi
266
+ done
267
+ return 0
268
+ }
269
+
270
+ dependencies_are_ready() {
271
+ local workspace
272
+ [ -f "$WORK_DIR/node_modules/.modules.yaml" ] || return 1
273
+ for workspace in client server; do
274
+ if [ -f "$PROJECT_ROOT/$workspace/package.json" ] &&
275
+ [ ! -f "$WORK_DIR/$workspace/node_modules/.modules.yaml" ]; then
276
+ return 1
277
+ fi
278
+ done
279
+ return 0
280
+ }
281
+
282
+ # 判断顺序按代价从小到大排。指纹要读云盘上的 lockfile 和整个 patches/,所以放最后:只有其余
283
+ # 条件都满足、确实可能复用时才付这份开销。build.sh 和 prepare.sh 不设开关,第一行就返回了。
284
+ install_can_be_reused() {
285
+ local installed_fingerprint=""
286
+
287
+ [ "${COZE_REUSE_NODE_MODULES:-}" = "1" ] || return 1
288
+ install_requires_refresh "$@" && return 1
289
+ [ -f "$INSTALL_STATE_FILE" ] || return 1
290
+ IFS= read -r installed_fingerprint < "$INSTALL_STATE_FILE" || return 1
291
+ dependencies_are_ready || return 1
292
+ install_reuse_is_safe || return 1
293
+ [ "$installed_fingerprint" = "$(calculate_install_fingerprint "$@")" ] || return 1
294
+ return 0
295
+ }
296
+
297
+ write_install_state() {
298
+ local fingerprint="$1" temporary_state
299
+ temporary_state="$(mktemp "$WORK_DIR/.install-state.XXXXXX")"
300
+ printf '%s\n' "$fingerprint" > "$temporary_state"
301
+ mv -f "$temporary_state" "$INSTALL_STATE_FILE"
302
+ }
303
+
144
304
  copy_install_metadata() {
145
305
  local filename source_file target_file workspace
146
306
  for filename in pnpm-lock.yaml pnpm-workspace.yaml .npmrc; do
@@ -190,16 +350,26 @@ link_node_modules() {
190
350
  cleanup_legacy_shadow_project
191
351
  copy_install_metadata
192
352
 
193
- echo "[node_modules] Installing dependencies in $WORK_DIR"
194
- if ! (cd "$WORK_DIR" && pnpm install "$@"); then
195
- exit 1
196
- fi
353
+ if install_can_be_reused "$@"; then
354
+ echo "[node_modules] Reusing unchanged dependencies in $WORK_DIR"
355
+ else
356
+ # A stale state file must not survive a failed install.
357
+ rm -f "$INSTALL_STATE_FILE"
358
+ echo "[node_modules] Installing dependencies in $WORK_DIR"
359
+ if ! (cd "$WORK_DIR" && pnpm install "$@"); then
360
+ exit 1
361
+ fi
362
+
363
+ if [ -f "$WORK_DIR/pnpm-lock.yaml" ]; then
364
+ temporary_lockfile="$(mktemp "$PROJECT_ROOT/.pnpm-lock.yaml.XXXXXX")"
365
+ cp "$WORK_DIR/pnpm-lock.yaml" "$temporary_lockfile"
366
+ chmod 0644 "$temporary_lockfile"
367
+ mv -f "$temporary_lockfile" "$PROJECT_ROOT/pnpm-lock.yaml"
368
+ fi
197
369
 
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"
370
+ # pnpm may have rewritten the lockfile, so record the fingerprint of the tree as it
371
+ # now stands. Otherwise the next run would see a changed input and reinstall.
372
+ write_install_state "$(calculate_install_fingerprint "$@")"
203
373
  fi
204
374
 
205
375
  link_node_modules "$PROJECT_ROOT" "$WORK_DIR/node_modules"
package/lib/cli.js CHANGED
@@ -2114,7 +2114,7 @@ const EventBuilder = {
2114
2114
  };
2115
2115
 
2116
2116
  var name = "@coze-arch/cli";
2117
- var version = "0.1.8-alpha.a5ed27";
2117
+ var version = "0.1.8-alpha.a87968";
2118
2118
  var description = "coze coding devtools cli";
2119
2119
  var license = "MIT";
2120
2120
  var author = "fanwenjie.fe@bytedance.com";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coze-arch/cli",
3
- "version": "0.1.8-alpha.a5ed27",
3
+ "version": "0.1.8-alpha.a87968",
4
4
  "private": false,
5
5
  "description": "coze coding devtools cli",
6
6
  "license": "MIT",
@@ -1,194 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Run one-shot Taro builds from a local shadow workspace when the project lives
3
- # on Coze Drive. This avoids repeated compiler stat/read traffic against the
4
- # network filesystem while keeping final artifacts in the project directory.
5
- set -Eeuo pipefail
6
-
7
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
8
- ROOT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd -P)"
9
- PROJECT_ROOT="$(cd "${COZE_WORKSPACE_PATH:-$ROOT_DIR}" && pwd -P)"
10
-
11
- if [ "$#" -eq 0 ]; then
12
- set -- pnpm build
13
- fi
14
-
15
- is_under_root() {
16
- local path="$1"
17
- local root="$2"
18
- [ -n "$root" ] || return 1
19
- case "$path" in
20
- "$root"|"$root"/*) return 0 ;;
21
- *) return 1 ;;
22
- esac
23
- }
24
-
25
- is_coze_drive_workspace() {
26
- local path="$1"
27
- local root
28
- local candidates=()
29
-
30
- if [ -n "${COZE_DRIVE_ROOT:-}" ]; then
31
- candidates+=("$COZE_DRIVE_ROOT")
32
- fi
33
- candidates+=("/Coze/Drive")
34
-
35
- for root in "${candidates[@]}"; do
36
- if [ -d "$root" ]; then
37
- root="$(cd "$root" && pwd -P)"
38
- else
39
- root="${root%/}"
40
- fi
41
- if is_under_root "$path" "$root"; then
42
- return 0
43
- fi
44
- done
45
-
46
- return 1
47
- }
48
-
49
- prepare_dependencies() {
50
- if [ "${COZE_SKIP_PREPARE_NODE_MODULES:-0}" = "1" ]; then
51
- return
52
- fi
53
- echo "[local-build] Preparing dependencies..."
54
- bash "$ROOT_DIR/.cozeproj/scripts/prepare-node-modules.sh" --prefer-frozen-lockfile --prefer-offline
55
- }
56
-
57
- if ! is_coze_drive_workspace "$PROJECT_ROOT"; then
58
- prepare_dependencies
59
- cd "$PROJECT_ROOT"
60
- "$@"
61
- exit $?
62
- fi
63
-
64
- command -v rsync >/dev/null 2>&1 || {
65
- echo "[local-build] rsync is required for Coze Drive local builds" >&2
66
- exit 1
67
- }
68
-
69
- prepare_dependencies
70
-
71
- BUILD_ROOT="${COZE_LOCAL_BUILD_ROOT:-/tmp/coze-taro-build}"
72
- PROJECT_ID="$(basename "$PROJECT_ROOT")-$(printf '%s' "$PROJECT_ROOT" | cksum | awk '{print $1}')"
73
- WORK_DIR="$BUILD_ROOT/$PROJECT_ID"
74
- LOCAL_WORKSPACE="$WORK_DIR/workspace"
75
- LOCK_DIR="$WORK_DIR.lock"
76
-
77
- mkdir -p "$BUILD_ROOT"
78
- if is_coze_drive_workspace "$(cd "$BUILD_ROOT" && pwd -P)"; then
79
- echo "[local-build] COZE_LOCAL_BUILD_ROOT must not be on Coze Drive: $BUILD_ROOT" >&2
80
- exit 1
81
- fi
82
-
83
- while ! mkdir "$LOCK_DIR" 2>/dev/null; do
84
- echo "[local-build] Waiting for another local build for this project..."
85
- sleep 1
86
- done
87
-
88
- cleanup() {
89
- if [ -n "${SOURCE_SYNC_PID:-}" ]; then
90
- kill "$SOURCE_SYNC_PID" 2>/dev/null || true
91
- fi
92
- rm -rf "$LOCAL_WORKSPACE"
93
- rmdir "$WORK_DIR" 2>/dev/null || true
94
- rmdir "$LOCK_DIR" 2>/dev/null || true
95
- }
96
- trap cleanup EXIT
97
-
98
- mkdir -p "$LOCAL_WORKSPACE"
99
-
100
- sync_source_to_local() {
101
- rsync -rlgoD --checksum --delete \
102
- --exclude '/.git/' \
103
- --exclude '/node_modules/' \
104
- --exclude '/server/node_modules/' \
105
- --exclude '/logs/' \
106
- --exclude '/dist/' \
107
- --exclude '/dist-web/' \
108
- --exclude '/dist-tt/' \
109
- --exclude '/server/dist/' \
110
- --exclude '/.next/' \
111
- --exclude '/.nuxt/' \
112
- --exclude '/.output/' \
113
- --exclude '/coverage/' \
114
- "$PROJECT_ROOT/" "$LOCAL_WORKSPACE/"
115
- }
116
-
117
- echo "[local-build] Syncing source to $LOCAL_WORKSPACE"
118
- sync_source_to_local
119
-
120
- link_node_modules() {
121
- local source_node_modules="$1"
122
- local shadow_parent="$2"
123
- local shadow_node_modules="$shadow_parent/node_modules"
124
- local target
125
-
126
- if [ ! -e "$source_node_modules" ] && [ ! -L "$source_node_modules" ]; then
127
- return
128
- fi
129
-
130
- if [ -L "$source_node_modules" ]; then
131
- target="$(readlink "$source_node_modules")"
132
- else
133
- target="$source_node_modules"
134
- fi
135
-
136
- mkdir -p "$shadow_parent"
137
- rm -rf "$shadow_node_modules"
138
- ln -s "$target" "$shadow_node_modules"
139
- }
140
-
141
- link_node_modules "$PROJECT_ROOT/node_modules" "$LOCAL_WORKSPACE"
142
- for workspace in server; do
143
- if [ -f "$PROJECT_ROOT/$workspace/package.json" ]; then
144
- link_node_modules "$PROJECT_ROOT/$workspace/node_modules" "$LOCAL_WORKSPACE/$workspace"
145
- fi
146
- done
147
-
148
- SOURCE_SYNC_PID=""
149
- if [ "${COZE_SYNC_SOURCE_TO_LOCAL:-0}" = "1" ]; then
150
- (
151
- while true; do
152
- sleep "${COZE_SYNC_INTERVAL_SECONDS:-1}"
153
- sync_source_to_local
154
- done
155
- ) &
156
- SOURCE_SYNC_PID=$!
157
- fi
158
-
159
- rm -rf \
160
- "$LOCAL_WORKSPACE/dist" \
161
- "$LOCAL_WORKSPACE/dist-web" \
162
- "$LOCAL_WORKSPACE/dist-tt" \
163
- "$LOCAL_WORKSPACE/server/dist"
164
-
165
- echo "[local-build] Building on local disk..."
166
- (
167
- cd "$LOCAL_WORKSPACE"
168
- export COZE_WORKSPACE_PATH="$LOCAL_WORKSPACE"
169
- "$@"
170
- )
171
-
172
- sync_output_back() {
173
- local output_name="$1"
174
- local local_output="$LOCAL_WORKSPACE/$output_name"
175
- local project_output="$PROJECT_ROOT/$output_name"
176
-
177
- if [ ! -e "$local_output" ]; then
178
- return
179
- fi
180
-
181
- if [ -L "$project_output" ] || [ -e "$project_output" ]; then
182
- rm -rf "$project_output"
183
- fi
184
-
185
- mkdir -p "$(dirname "$project_output")"
186
- rsync -a --delete "$local_output/" "$project_output/"
187
- }
188
-
189
- echo "[local-build] Syncing build artifacts back to $PROJECT_ROOT"
190
- sync_output_back dist
191
- sync_output_back dist-web
192
- sync_output_back dist-tt
193
- sync_output_back server/dist
194
- echo "[local-build] Build artifacts are ready in the project directory"