makdoong2-team 1.3.0 → 1.3.1
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/README.md +38 -2
- package/package.json +3 -2
- package/scripts/rollback-commits.sh +7 -1
- package/scripts/run-tests.mjs +81 -0
- package/scripts/test-ubuntu.sh +155 -0
package/README.md
CHANGED
|
@@ -176,10 +176,46 @@ npm run release:major # 0.2.3 → 1.0.0 (breaking)
|
|
|
176
176
|
## Testing
|
|
177
177
|
|
|
178
178
|
```bash
|
|
179
|
-
npm test # build +
|
|
180
|
-
npm run test:
|
|
179
|
+
npm test # build + 43개 단계 전체 (scripts/run-tests.mjs)
|
|
180
|
+
npm run test:ubuntu # 위와 동일한 스위트를 Ubuntu 24.04 컨테이너에서 실행
|
|
181
|
+
npm run test:install # 설치 라이브러리 테스트만
|
|
181
182
|
```
|
|
182
183
|
|
|
184
|
+
`npm test` 는 `scripts/run-tests.mjs` 로 각 단계를 **순차 실행하되 실패해도 멈추지 않고**
|
|
185
|
+
끝까지 돌린 뒤 실패 목록을 모아 보고한다. 이전의 `&&` 체인은 앞 단계가 하나만 깨져도
|
|
186
|
+
뒤 단계가 전부 미실행 상태가 되어, 실제로 여러 실패가 서로를 가려왔다.
|
|
187
|
+
|
|
188
|
+
### Ubuntu 에서 실행 — `npm run test:ubuntu`
|
|
189
|
+
|
|
190
|
+
플러그인은 Ubuntu 에서 개발·운영되지만 테스트는 macOS 에서도 자주 돌린다. 두 환경은
|
|
191
|
+
다음 지점에서 갈리며, 셋 다 실제로 테스트 실패로 나타난 적이 있다.
|
|
192
|
+
|
|
193
|
+
| 갈리는 지점 | macOS | Linux |
|
|
194
|
+
|---|---|---|
|
|
195
|
+
| `isalnum()` (UTF-8 로케일, 상위 바이트) | `0xEA` 를 alnum 으로 보고 → `$VAR한글` 이 깨짐 | 상위 바이트는 non-alnum → 정상 |
|
|
196
|
+
| `os.tmpdir()` | `/var/folders/…` (`/var` 는 심볼릭 링크) | `/tmp` (실제 디렉토리) |
|
|
197
|
+
| 기본 bash | 3.2 | 5.x |
|
|
198
|
+
|
|
199
|
+
`scripts/test-ubuntu.sh` 가 `Dockerfile.test` (ubuntu:24.04 + Node 22) 를 빌드해 같은
|
|
200
|
+
스위트를 컨테이너에서 돌린다. Docker 또는 OrbStack 이 필요하다. 호스트 uid 로 실행하므로
|
|
201
|
+
root 소유 파일이 생기지 않고, Linux 용 `node_modules` 는 `.docker-test/` (gitignore) 에
|
|
202
|
+
따로 두어 호스트 것을 덮어쓰지 않는다.
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
npm run test:ubuntu # 전체 스위트
|
|
206
|
+
bash scripts/test-ubuntu.sh npm run build # 임의 명령
|
|
207
|
+
bash scripts/test-ubuntu.sh bash # 컨테이너 셸 (디버깅)
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**자동 정리** — 컨테이너는 `docker run --rm` 으로 종료 즉시 제거된다. docker 데몬
|
|
211
|
+
(OrbStack/colima VM) 이 꺼져 있어서 스크립트가 직접 기동한 경우에는 테스트가 끝날 때
|
|
212
|
+
자동으로 다시 끈다. 실행 전부터 떠 있었다면 다른 작업을 끊지 않도록 그대로 둔다.
|
|
213
|
+
`MAKDOONG2_KEEP_DOCKER=1` 로 자동 종료를 끌 수 있다.
|
|
214
|
+
|
|
215
|
+
**주의**: Ubuntu 러너는 macOS 전용 버그를 잡지 못한다 — 위 표의 첫 줄이 정확히 그런
|
|
216
|
+
경우다. 그 부류는 `test/shell-portability.test.mjs` 가 정적으로 검사한다 (`$VAR` 뒤에
|
|
217
|
+
non-ASCII 가 붙으면 실패, `${VAR}` 로 감쌀 것).
|
|
218
|
+
|
|
183
219
|
`.husky/pre-push` 가 unit test 실행 + version 변경 감지 시 자동 배포 훅을 트리거한다.
|
|
184
220
|
|
|
185
221
|
## 문서
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "makdoong2-team",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -69,9 +69,10 @@
|
|
|
69
69
|
"postinstall": "node postinstall.mjs",
|
|
70
70
|
"prepare": "husky install",
|
|
71
71
|
"test": "XDG_CONFIG_HOME=\"$(mktemp -d)\" XDG_CACHE_HOME=\"$(mktemp -d)\" npm run test:all",
|
|
72
|
-
"test:all": "npm run build &&
|
|
72
|
+
"test:all": "npm run build && node scripts/run-tests.mjs",
|
|
73
73
|
"test:install": "node test/install-lib.test.mjs",
|
|
74
74
|
"test:postinstall": "node scripts/test-postinstall.mjs",
|
|
75
|
+
"test:ubuntu": "bash scripts/test-ubuntu.sh",
|
|
75
76
|
"release": "bash scripts/release.sh",
|
|
76
77
|
"release:patch": "bash scripts/release.sh patch",
|
|
77
78
|
"release:minor": "bash scripts/release.sh minor",
|
|
@@ -26,7 +26,13 @@ git -C "$WT" merge-base --is-ancestor "$BASE" HEAD 2>/dev/null || {
|
|
|
26
26
|
# 커밋만 취소, 변경은 보존 (soft reset)
|
|
27
27
|
N=$(git -C "$WT" rev-list --count "$BASE..HEAD" 2>/dev/null || echo 0)
|
|
28
28
|
git -C "$WT" reset --soft "$BASE"
|
|
29
|
-
|
|
29
|
+
# ${N} and ${BASE} must stay braced. Unbraced, bash reads the variable name with
|
|
30
|
+
# legal_variable_char() == isalnum() one byte at a time, and Darwin libc reports
|
|
31
|
+
# 0xEA — the lead byte of the Hangul counter suffix that follows — as alnum in
|
|
32
|
+
# UTF-8 locales. The name absorbs that byte, becomes unbound, and set -u aborts.
|
|
33
|
+
# glibc keeps high bytes non-alnum, so this only bites on macOS.
|
|
34
|
+
# Guarded by test/shell-portability.test.mjs.
|
|
35
|
+
echo "[rollback] HEAD를 ${BASE} 로 soft-reset 완료 (취소된 커밋 ${N}개, working tree/index 변경은 보존됨)"
|
|
30
36
|
|
|
31
37
|
# 3_delivery.commit 관련 state 초기화 (base_sha는 보존 — 동일 기준점으로 재커밋)
|
|
32
38
|
"$HERE/state.sh" set "$ISSUE" '.stages."3_delivery".substages."commit".done' 'false' >/dev/null
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// scripts/run-tests.mjs — 전체 테스트를 순차 실행하고 실패를 모아서 보고한다.
|
|
3
|
+
//
|
|
4
|
+
// 왜 `&&` 체인을 대체하는가:
|
|
5
|
+
// 기존 test:all 은 42개 단계를 `&&` 로 이어 붙였다. 앞 단계가 하나라도 실패하면
|
|
6
|
+
// 뒤 단계는 아예 실행되지 않는다. 실제로 tmux-monitor 실패가 그 뒤의
|
|
7
|
+
// rollback-commits / worktree-sync-gate 실패를 오래 가려왔고, gate-policy-test 가
|
|
8
|
+
// 깨져 있던 동안에는 사실상 전 구간이 미실행 상태였다.
|
|
9
|
+
//
|
|
10
|
+
// 순차 실행은 유지한다 (병렬화하지 않는다). npm test 가 XDG_CONFIG_HOME 을 단일
|
|
11
|
+
// 임시 디렉토리로 고정하므로, 설치 계열 테스트를 동시에 돌리면 서로의 config 를
|
|
12
|
+
// 덮어써 경합이 난다.
|
|
13
|
+
//
|
|
14
|
+
// 종료 코드: 실패 단계가 하나라도 있으면 1.
|
|
15
|
+
|
|
16
|
+
import { spawnSync } from "node:child_process";
|
|
17
|
+
|
|
18
|
+
const STEPS = [
|
|
19
|
+
"bash scripts/lint-agent-prompts.sh",
|
|
20
|
+
"node --test test/shell-portability.test.mjs",
|
|
21
|
+
"node scripts/smoke-test.mjs",
|
|
22
|
+
"bash scripts/gate-policy-test.sh",
|
|
23
|
+
"node test/install-lib.test.mjs",
|
|
24
|
+
"node test/skill-mcp-registry.test.mjs",
|
|
25
|
+
"node --test test/state-write-guard.test.mjs",
|
|
26
|
+
"node --test test/state-sh-schema.test.mjs",
|
|
27
|
+
"node --test test/state-sh-init-review-shape.test.mjs",
|
|
28
|
+
"node --test test/doctor-phantom-scan.test.mjs",
|
|
29
|
+
"node --test test/mcp-secret-injector.test.mjs",
|
|
30
|
+
"node --test test/poll-sub-session.test.mjs",
|
|
31
|
+
"node --test test/tmux-monitor.test.mjs",
|
|
32
|
+
"node --test test/gate-already-done-block.test.mjs",
|
|
33
|
+
"node --test test/gate-hybrid-first-entry.test.mjs",
|
|
34
|
+
"node --test test/gate-post-pr-verify.test.mjs",
|
|
35
|
+
"node --test test/gate-post-review-verify.test.mjs",
|
|
36
|
+
"node --test test/gate-requirements-quality.test.mjs",
|
|
37
|
+
"node --test test/worktree-sync-gate.test.mjs",
|
|
38
|
+
"node --test test/planner-prompt-early-exit.test.mjs",
|
|
39
|
+
"node --test test/plugin-bug-fixes.test.mjs",
|
|
40
|
+
"node --test test/omo-tmux-detection.test.mjs",
|
|
41
|
+
"node --test test/empty-output-done-override.test.mjs",
|
|
42
|
+
"node --test test/tmux-monitor-orphan.test.mjs",
|
|
43
|
+
"node --test test/poll-permission-scope.test.mjs",
|
|
44
|
+
"node --test test/dispatch-review-fixes.test.mjs",
|
|
45
|
+
"node --test test/dispatch-stage-redispatch.test.mjs",
|
|
46
|
+
"node --test test/dispatch-stage-dev-resume-prompt.test.mjs",
|
|
47
|
+
"node --test test/logger.test.mjs",
|
|
48
|
+
"node --test test/redact-secrets.test.mjs",
|
|
49
|
+
"node --test test/commit-atomicity-verify.test.mjs",
|
|
50
|
+
"node --test test/verdict-reason-injection.test.mjs",
|
|
51
|
+
"node --test test/verdict-hash-normalize.test.mjs",
|
|
52
|
+
"node --test test/session-index-fallback.test.mjs",
|
|
53
|
+
"node --test test/plugin-exports-shape.test.mjs",
|
|
54
|
+
"node --test test/stale-worktree-recovery.test.mjs",
|
|
55
|
+
"node --test test/rollback-commits.test.mjs",
|
|
56
|
+
"node --test test/config-dir-home-fallback.test.mjs",
|
|
57
|
+
"node --test test/state-path-migration.test.mjs",
|
|
58
|
+
"node --test test/state-path-relative.test.mjs",
|
|
59
|
+
"node --test test/with-fallback-no-bun.test.mjs",
|
|
60
|
+
"node --test test/doctor-exit-code.test.mjs",
|
|
61
|
+
"node --test test/example-config-portability.test.mjs"
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
const results = [];
|
|
65
|
+
for (const [i, step] of STEPS.entries()) {
|
|
66
|
+
const [cmd, ...args] = step.split(" ");
|
|
67
|
+
process.stdout.write(`\n\u001b[36m[${String(i + 1).padStart(2)}/${STEPS.length}]\u001b[0m ${step}\n`);
|
|
68
|
+
const r = spawnSync(cmd, args, { stdio: "inherit" });
|
|
69
|
+
const code = r.status === null ? 1 : r.status;
|
|
70
|
+
results.push({ step, code });
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const failed = results.filter((r) => r.code !== 0);
|
|
74
|
+
process.stdout.write(`\n${"=".repeat(70)}\n`);
|
|
75
|
+
if (failed.length === 0) {
|
|
76
|
+
process.stdout.write(`\u001b[32m[run-tests] ${results.length}개 단계 전부 통과\u001b[0m\n`);
|
|
77
|
+
process.exit(0);
|
|
78
|
+
}
|
|
79
|
+
process.stdout.write(`\u001b[31m[run-tests] ${failed.length}/${results.length} 단계 실패:\u001b[0m\n`);
|
|
80
|
+
for (const f of failed) process.stdout.write(` \u001b[31m\u2716\u001b[0m ${f.step} (exit ${f.code})\n`);
|
|
81
|
+
process.exit(1);
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# test-ubuntu.sh — run the test suite inside the Ubuntu image the plugin targets.
|
|
3
|
+
#
|
|
4
|
+
# 사용법:
|
|
5
|
+
# scripts/test-ubuntu.sh # npm test 전체 실행
|
|
6
|
+
# scripts/test-ubuntu.sh npm run build # 임의 명령 실행
|
|
7
|
+
# scripts/test-ubuntu.sh bash # 컨테이너 셸 진입 (디버깅)
|
|
8
|
+
#
|
|
9
|
+
# 왜 필요한가:
|
|
10
|
+
# 플러그인은 Ubuntu 에서 개발·운영되지만 테스트는 macOS 에서도 자주 돌린다.
|
|
11
|
+
# 두 환경은 libc ctype 테이블, /var 심볼릭 링크, bash 3.2 vs 5.x 에서 갈리며
|
|
12
|
+
# 실제로 이 세 가지가 모두 테스트 실패로 나타난 적이 있다. 이 스크립트는
|
|
13
|
+
# Linux 쪽 결과를 어느 호스트에서든 재현할 수 있게 한다.
|
|
14
|
+
#
|
|
15
|
+
# 동작:
|
|
16
|
+
# - 저장소를 /w 에 bind-mount 한다.
|
|
17
|
+
# - node_modules 는 호스트의 .docker-test/node_modules 를 겹쳐 mount 한다.
|
|
18
|
+
# (macOS 용 node_modules 를 덮어쓰지 않기 위함 — 네이티브 바이너리가 다르다)
|
|
19
|
+
# - 호스트 uid 를 담은 /etc/passwd 를 만들어 넣는다. 이게 없으면 os.homedir()
|
|
20
|
+
# 가 HOME 미설정 시 getpwuid 폴백에서 ENOENT 로 죽어 실제 Ubuntu 장비에서는
|
|
21
|
+
# 나지 않는 실패가 발생한다 (test/config-dir-home-fallback.test.mjs).
|
|
22
|
+
# - package-lock.json 이 바뀐 경우에만 npm ci 를 다시 돌린다.
|
|
23
|
+
# - 호스트 uid/gid 로 실행해 root 소유 파일이 생기지 않게 한다.
|
|
24
|
+
#
|
|
25
|
+
# 정리(cleanup):
|
|
26
|
+
# - 컨테이너는 `docker run --rm` 으로 종료 즉시 제거된다.
|
|
27
|
+
# - docker 데몬(OrbStack/colima VM)이 꺼져 있어서 이 스크립트가 직접 기동한
|
|
28
|
+
# 경우에만 끝날 때 다시 끈다. 이미 떠 있었다면 사용자의 다른 작업을 끊지
|
|
29
|
+
# 않도록 그대로 둔다. MAKDOONG2_KEEP_DOCKER=1 로 자동 종료를 끌 수 있다.
|
|
30
|
+
#
|
|
31
|
+
# 주의: dist/ 는 mount 된 저장소 안에 다시 빌드된다. tsc 산출물은 플랫폼
|
|
32
|
+
# 독립적이므로 macOS 빌드와 내용이 같다.
|
|
33
|
+
set -euo pipefail
|
|
34
|
+
|
|
35
|
+
IMAGE="${MAKDOONG2_TEST_IMAGE:-makdoong2-team-test:ubuntu-24.04}"
|
|
36
|
+
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
37
|
+
WORKDIR="$ROOT/.docker-test"
|
|
38
|
+
MODULES="$WORKDIR/node_modules"
|
|
39
|
+
PASSWD="$WORKDIR/passwd"
|
|
40
|
+
|
|
41
|
+
RED=$'\033[0;31m'; GREEN=$'\033[0;32m'; YELLOW=$'\033[0;33m'; CYAN=$'\033[0;36m'; RESET=$'\033[0m'
|
|
42
|
+
log() { printf "%s[test-ubuntu]%s %s\n" "$CYAN" "$RESET" "$*"; }
|
|
43
|
+
warn() { printf "%s[warn]%s %s\n" "$YELLOW" "$RESET" "$*"; }
|
|
44
|
+
err() { printf "%s[error]%s %s\n" "$RED" "$RESET" "$*" >&2; }
|
|
45
|
+
|
|
46
|
+
# ---------- docker 데몬 준비 ----------
|
|
47
|
+
STARTED_DAEMON=0
|
|
48
|
+
DAEMON_KIND=""
|
|
49
|
+
|
|
50
|
+
cleanup() {
|
|
51
|
+
local status=$?
|
|
52
|
+
# 컨테이너는 --rm 이 이미 지웠다. 여기서는 "우리가 켠 것"만 되돌린다.
|
|
53
|
+
if [ "$STARTED_DAEMON" = "1" ]; then
|
|
54
|
+
if [ "${MAKDOONG2_KEEP_DOCKER:-0}" = "1" ]; then
|
|
55
|
+
log "MAKDOONG2_KEEP_DOCKER=1 — $DAEMON_KIND 를 켠 채로 둔다"
|
|
56
|
+
else
|
|
57
|
+
log "$DAEMON_KIND 종료 (이 스크립트가 기동했으므로 원상복구)"
|
|
58
|
+
case "$DAEMON_KIND" in
|
|
59
|
+
orbstack) orb stop >/dev/null 2>&1 || warn "orb stop 실패 — 수동으로 'orb stop'" ;;
|
|
60
|
+
colima) colima stop >/dev/null 2>&1 || warn "colima stop 실패 — 수동으로 'colima stop'" ;;
|
|
61
|
+
esac
|
|
62
|
+
fi
|
|
63
|
+
fi
|
|
64
|
+
return "$status"
|
|
65
|
+
}
|
|
66
|
+
trap cleanup EXIT
|
|
67
|
+
|
|
68
|
+
if ! command -v docker >/dev/null 2>&1; then
|
|
69
|
+
err "docker 를 찾을 수 없다. Docker / OrbStack / colima 중 하나가 필요하다."
|
|
70
|
+
exit 1
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
if docker info >/dev/null 2>&1; then
|
|
74
|
+
log "docker 데몬 이미 실행 중 — 종료하지 않고 그대로 둔다"
|
|
75
|
+
else
|
|
76
|
+
if command -v orb >/dev/null 2>&1; then
|
|
77
|
+
DAEMON_KIND="orbstack"; log "docker 데몬이 꺼져 있다 — OrbStack 기동"; orb start >/dev/null 2>&1 || true
|
|
78
|
+
elif command -v colima >/dev/null 2>&1; then
|
|
79
|
+
DAEMON_KIND="colima"; log "docker 데몬이 꺼져 있다 — colima 기동"; colima start >/dev/null 2>&1 || true
|
|
80
|
+
else
|
|
81
|
+
err "docker 데몬에 연결할 수 없고 자동 기동 수단(orb/colima)도 없다. Docker Desktop 을 먼저 실행하라."
|
|
82
|
+
exit 1
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
for _ in $(seq 1 40); do
|
|
86
|
+
docker info >/dev/null 2>&1 && break
|
|
87
|
+
sleep 2
|
|
88
|
+
done
|
|
89
|
+
if ! docker info >/dev/null 2>&1; then
|
|
90
|
+
err "$DAEMON_KIND 기동 후에도 docker 데몬에 연결할 수 없다."
|
|
91
|
+
exit 1
|
|
92
|
+
fi
|
|
93
|
+
STARTED_DAEMON=1 # 여기서부터 cleanup 이 되돌릴 책임을 진다
|
|
94
|
+
log "$DAEMON_KIND 기동 완료 — 테스트 종료 시 자동으로 다시 끈다"
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
# ---------- 이미지 + 마운트 준비 ----------
|
|
98
|
+
log "이미지 빌드: $IMAGE"
|
|
99
|
+
docker build --quiet -f "$ROOT/Dockerfile.test" -t "$IMAGE" "$ROOT" >/dev/null
|
|
100
|
+
|
|
101
|
+
mkdir -p "$MODULES"
|
|
102
|
+
|
|
103
|
+
# 컨테이너 안에서 호스트 uid 가 실재하는 사용자로 보이도록 최소 passwd 를 만든다.
|
|
104
|
+
{
|
|
105
|
+
echo "root:x:0:0:root:/root:/bin/bash"
|
|
106
|
+
echo "tester:x:$(id -u):$(id -g):tester:/tmp:/bin/bash"
|
|
107
|
+
} > "$PASSWD"
|
|
108
|
+
|
|
109
|
+
# 컨테이너 안에서 실행할 명령. 인자가 없으면 전체 테스트.
|
|
110
|
+
if [ "$#" -eq 0 ]; then
|
|
111
|
+
INNER_CMD="npm test"
|
|
112
|
+
else
|
|
113
|
+
INNER_CMD="$(printf '%q ' "$@")"
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
# ---------- 실행 ----------
|
|
117
|
+
log "실행: $INNER_CMD"
|
|
118
|
+
STATUS=0
|
|
119
|
+
docker run --rm -t \
|
|
120
|
+
-v "$ROOT:/w" \
|
|
121
|
+
-v "$MODULES:/w/node_modules" \
|
|
122
|
+
-v "$PASSWD:/etc/passwd:ro" \
|
|
123
|
+
-w /w \
|
|
124
|
+
--user "$(id -u):$(id -g)" \
|
|
125
|
+
-e HOME=/tmp \
|
|
126
|
+
-e npm_config_cache=/tmp/.npm \
|
|
127
|
+
-e npm_config_update_notifier=false \
|
|
128
|
+
-e CI=true \
|
|
129
|
+
"$IMAGE" \
|
|
130
|
+
bash -euo pipefail -c '
|
|
131
|
+
git config --global --add safe.directory /w
|
|
132
|
+
git config --global user.email "test@makdoong2.local"
|
|
133
|
+
git config --global user.name "makdoong2 test"
|
|
134
|
+
|
|
135
|
+
# package-lock.json 이 바뀌었을 때만 npm ci. 해시를 node_modules 안에 남긴다.
|
|
136
|
+
LOCK_HASH="$(sha256sum package-lock.json | cut -d" " -f1)"
|
|
137
|
+
STAMP="node_modules/.lock-hash"
|
|
138
|
+
if [ ! -f "$STAMP" ] || [ "$(cat "$STAMP")" != "$LOCK_HASH" ]; then
|
|
139
|
+
echo "[test-ubuntu] npm ci (lockfile 변경 감지)"
|
|
140
|
+
npm ci --no-audit --no-fund
|
|
141
|
+
printf "%s" "$LOCK_HASH" > "$STAMP"
|
|
142
|
+
else
|
|
143
|
+
echo "[test-ubuntu] node_modules 재사용 (lockfile 변경 없음)"
|
|
144
|
+
fi
|
|
145
|
+
|
|
146
|
+
echo "[test-ubuntu] node $(node -v) | bash $(bash --version | head -1 | sed "s/.*version //;s/ .*//") | tmux $(tmux -V | cut -d" " -f2) | LANG=$LANG"
|
|
147
|
+
'"$INNER_CMD"'
|
|
148
|
+
' || STATUS=$?
|
|
149
|
+
|
|
150
|
+
if [ "$STATUS" -eq 0 ]; then
|
|
151
|
+
printf "%s[test-ubuntu]%s %sUbuntu 실행 성공%s\n" "$CYAN" "$RESET" "$GREEN" "$RESET"
|
|
152
|
+
else
|
|
153
|
+
err "Ubuntu 실행 실패 (exit $STATUS)"
|
|
154
|
+
fi
|
|
155
|
+
exit "$STATUS"
|