Skip to content

Commit ad5ea11

Browse files
author
lvsi
committed
doc: make doc better
1 parent 8481e6e commit ad5ea11

4 files changed

Lines changed: 150 additions & 0 deletions

File tree

‎.work/extra/doc/3.DEVELOPMENT_EN.md‎

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,3 +312,60 @@ Each service has a ```data``` directory, which is the only data channel from the
312312

313313
- The persistent data of databases like MySQL and Redis will be mounted to their ```data``` directories.
314314
- Projects based on environments such as Python/PHP/Go will be mounted to their ```data``` directories.
315+
316+
## 6. Service Lifecycle Hooks
317+
318+
### (1) What are hooks
319+
320+
Each service can drop up to 4 scripts into its `hooks/` directory; sparrow runs each at a fixed point in the start/stop flow:
321+
322+
| Script | When it runs |
323+
|---|---|
324+
| `before_start.sh` | **Before** the container starts (before pull/build and `docker compose up`) |
325+
| `after_start.sh` | **After** the container starts |
326+
| `before_stop.sh` | **Before** the container stops |
327+
| `after_stop.sh` | **After** the container stops |
328+
329+
Typical uses: generate or seed config files the container needs in `before_start.sh`; clean up scratch resources in `after_stop.sh`.
330+
331+
The dispatcher lives in the `sparrow` script: it looks for `{service}/hooks/{hook_type}.sh` and executes it if present. A missing file is treated as a no-op (not an error).
332+
333+
### (2) Hooks are sourced into the current shell — **do not use `exit`**
334+
335+
⚠️ **Important footgun**: hooks are not forked into a subshell. Sparrow runs them with `.` (source) inside its own main shell. The non-obvious consequence:
336+
337+
- Writing `exit 0` / `exit 1` in a hook **terminates the entire sparrow process**. The remaining steps (pull, build, `docker compose up`) are silently skipped, and the user sees what looks like a clean exit — very easy to misdiagnose.
338+
- To return early from a hook, use **`return 0` / `return 1`** instead. That only exits the sourced block; sparrow's main flow keeps going.
339+
340+
Reference pattern:
341+
342+
```sh
343+
#!/bin/sh
344+
345+
# include sdk of sparrow.
346+
. .work/include/sdk.sh
347+
348+
# Config already initialized — skip and let sparrow continue.
349+
if [ -f "./myservice/data/config.json" ]; then
350+
print_info "config already exists, skip init"
351+
return 0 # ← correct: exits the hook only
352+
fi
353+
354+
# ... initialization logic ...
355+
if ! some_init_command; then
356+
print_error "init failed"
357+
return 1 # ← still use return, even on the failure path
358+
fi
359+
```
360+
361+
By the same logic, **do not** use `set -e` or anything like `kill $$` inside a hook — they affect the parent shell. If you need to signal a precondition failure, log it with `print_error` and `return 1`.
362+
363+
### (3) What is available inside a hook
364+
365+
`sdk.sh` has already been sourced by the time your hook runs, so you can use sparrow's helpers directly:
366+
367+
- `print_info` / `print_warn` / `print_error` — colored log helpers
368+
- All exported variables from `.env` (including `ENABLE_SERVICE_LIST` and per-service `IMAGE_*_VERSION`)
369+
- `${SPARROW_BASE_PATH}` — project root
370+
371+
Re-sourcing the sdk inside a hook (as defensive guard) is safe.

‎.work/extra/doc/3.DEVELOPMENT_ZH.md‎

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,3 +312,60 @@ server {
312312

313313
- MySQL、Redis 等数据库的持久化数据会挂载到各自的 data 目录。
314314
- Python/PHP/Go 等开发环境项目也会挂载到对应的 data 目录。
315+
316+
## 6. 服务生命周期 hook
317+
318+
### (1) hook 是什么
319+
320+
每个服务的 `hooks/` 目录下可以放置 4 个脚本,分别在服务启停的关键节点被自动执行:
321+
322+
| 脚本 | 触发时机 |
323+
|---|---|
324+
| `before_start.sh` | 容器启动**之前**(pull/build 镜像、`docker compose up` 之前) |
325+
| `after_start.sh` | 容器启动**之后** |
326+
| `before_stop.sh` | 容器停止**之前** |
327+
| `after_stop.sh` | 容器停止**之后** |
328+
329+
典型用途:在 `before_start.sh` 里生成或初始化容器需要的配置文件、密钥;在 `after_stop.sh` 里清理临时资源。
330+
331+
调用入口在 `sparrow` 脚本中:找到名为 `{service}/hooks/{hook_type}.sh` 的文件就执行。文件不存在视为无操作,不报错。
332+
333+
### (2) hook 是被 source 进当前 shell 的,**不要在 hook 里用 `exit`**
334+
335+
⚠️ **重要陷阱**:hook 不是 fork 子进程执行,而是用 `.` (source) 在 sparrow 主 shell 里执行。这带来一个反直觉的后果:
336+
337+
- 在 hook 里写 `exit 0`/`exit 1` 会**直接终止 sparrow 整个进程**,后续的 pull、build、docker compose up 全部跳过,但用户看到的仍然是看似"正常退出"——非常隐蔽。
338+
- 想从 hook 中途返回,请使用 **`return 0` / `return 1`**,效果是退出当前 source 块,sparrow 主流程继续向下走。
339+
340+
参考写法:
341+
342+
```sh
343+
#!/bin/sh
344+
345+
# include sdk of sparrow.
346+
. .work/include/sdk.sh
347+
348+
# 如果配置已存在,跳过初始化(让 sparrow 继续后续步骤)。
349+
if [ -f "./myservice/data/config.json" ]; then
350+
print_info "config already exists, skip init"
351+
return 0 # ← 正确:仅退出 hook,sparrow 继续
352+
fi
353+
354+
# ... 初始化逻辑 ...
355+
if ! some_init_command; then
356+
print_error "init failed"
357+
return 1 # ← 即使想以失败状态退出,也用 return
358+
fi
359+
```
360+
361+
同样地,hook 内**不要**用 `set -e` 或调用 `kill $$` 之类会影响父 shell 的语句。需要校验前置条件失败就告警时,用 `print_error` + `return 1` 即可。
362+
363+
### (3) hook 里可以用什么
364+
365+
`sdk.sh` 在 hook 被执行前就已经 source 过了,因此 hook 内可以直接使用 sparrow 的工具函数,例如:
366+
367+
- `print_info` / `print_warn` / `print_error` —— 带颜色的日志输出
368+
- `.env` 中所有已 export 的变量(包括 `ENABLE_SERVICE_LIST`、各服务的 `IMAGE_*_VERSION` 等)
369+
- `${SPARROW_BASE_PATH}` —— 项目根目录
370+
371+
如果 hook 内还想再次主动 source sdk(例如为了防御性写法),重复 source 是安全的。
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,20 @@
11
## What is this directory
2+
23
This is the hook actions while the service/container start/stop.
4+
5+
### Important: hooks are sourced, not forked
6+
7+
Hooks are executed via `. filename` (source), not in a subshell. Therefore:
8+
9+
- **Do NOT use `exit 0` / `exit 1` in hooks** — it terminates the whole sparrow process.
10+
- **Use `return 0` / `return 1` instead** — it exits only the sourced block, and sparrow continues.
11+
- Do NOT use `set -e` inside hooks — it affects the parent shell.
12+
13+
### Available hooks
14+
15+
| Hook file | Timing |
16+
|---|---|
17+
| `before_start.sh` | Before container start (before pull/build and `docker compose up`) |
18+
| `after_start.sh` | After container start |
19+
| `before_stop.sh` | Before container stop |
20+
| `after_stop.sh` | After container stop |

‎openclaw/hooks/README.md‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,20 @@
11
## What is this directory
2+
23
This is the hook actions while the service/container start/stop.
4+
5+
### Important: hooks are sourced, not forked
6+
7+
Hooks are executed via `. filename` (source), not in a subshell. Therefore:
8+
9+
- **Do NOT use `exit 0` / `exit 1` in hooks** — it terminates the whole sparrow process.
10+
- **Use `return 0` / `return 1` instead** — it exits only the sourced block, and sparrow continues.
11+
- Do NOT use `set -e` inside hooks — it affects the parent shell.
12+
13+
### Available hooks
14+
15+
| Hook file | Timing |
16+
|---|---|
17+
| `before_start.sh` | Before container start (before pull/build and `docker compose up`) |
18+
| `after_start.sh` | After container start |
19+
| `before_stop.sh` | Before container stop |
20+
| `after_stop.sh` | After container stop |

0 commit comments

Comments
 (0)