diff --git a/README.md b/README.md index 07d23fe..ceea3f0 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,181 @@ 3. 如果你的服务端口与模板中的默认值不同,可在 `services` 变量里调整 `name:port` 列表,例如:`auth:5710`、`project:5711` 等。 4. 如需调整重试次数、间隔时间或检查路径,可在对应 `curl` 循环处按需修改脚本逻辑。 -## 其他文件/目录 -`actions/`、`docs/` 等其他文件或目录仅作内部使用或扩展示例,对实际在项目中使用上述两个模板不是必需的,你可以忽略它们。 +## 通用复用 Actions 说明(`common-workflows/actions`) + +本目录下包含了一组可在各个仓库中复用的 **Gitea / GitHub composite actions**,统一托管在: + +- `https://git.aiot.ml/Obscura/common-workflows` + +在具体仓库的 workflow 中,通过 `uses` 引用,例如: + +```yaml +uses: https://git.aiot.ml/Obscura/common-workflows/actions/@main +``` + +目前提供的通用 action 如下: + +- **build-images**:构建并验证 Docker 镜像 +- **deploy-docker**:使用 `docker compose` 部署服务 +- **api-test**:对一组后端 API 的 `/health` 做健康检查 +- **frontend-test**:对一组前端入口 URL 做可访问性检查 + +下面分别说明用法。 + +--- + +## `build-images` — Docker 镜像构建 + +**路径**:`actions/build-images/action.yml` +**作用**:在 Runner 上本地执行 `docker build`,并对生成的镜像做简单校验与清理悬空镜像。 + +**引用示例**(节选自 `broccoli-offline`): + +```yaml +- name: Build Docker image + uses: https://git.aiot.ml/Obscura/common-workflows/actions/build-images@main + with: + dockerfile: 'Dockerfile.api' + context: '.' + image_name: 'broccoli-api' + image_tag: 'v3.2.2' + enable_cache: 'true' + # build_args: | + # HTTP_PROXY=http://... + # HTTPS_PROXY=http://... +``` + +**输入参数**: + +- **`dockerfile`**(必填):Dockerfile 路径,相对于仓库根目录,例如 `Dockerfile`, `Dockerfile.api`。 +- **`context`**(可选,默认 `.`):构建上下文目录,相对于仓库根目录。 +- **`image_name`**(必填):镜像名称,不含 tag,例如 `broccoli-api`。 +- **`image_tag`**(必填):镜像 tag / 版本,例如 `v1.0.0`、`latest`。 +- **`enable_cache`**(可选,默认 `'true'`):是否启用构建缓存(当前主要依赖 Runner 本地 docker layer cache)。 +- **`build_args`**(可选):Docker build 参数,每行一个,格式 `KEY=VALUE`,会被拼接为 `--build-arg KEY=VALUE`。 + +**依赖环境**: + +- Runner 上需要已安装 `docker`,并有构建镜像的权限。 + +--- + +## `deploy-docker` — Docker Compose 部署 + +**路径**:`actions/deploy-docker/action.yml` +**作用**:在已经 checkout 好代码的前提下,使用 `docker compose` 停止旧服务并重新部署,最后校验服务状态。 + +**典型用法**(示例): + +```yaml +- name: Checkout code + uses: https://git.aiot.ml/Obscura/checkout@v4 + with: + ref: offline + sparse-checkout: | + .gitea/workflows/deploy-docker.yml + config/ + docker-compose-offline.yml + sparse-checkout-cone-mode: false + +- name: Deploy with reusable action + uses: https://git.aiot.ml/Obscura/common-workflows/actions/deploy-docker@main + with: + compose_file: 'docker-compose-offline.yml' + # compose_dir: 'config' # 可选:指定工作目录 + # startup_wait: '30' # 可选:启动后等待 30s 再校验 +``` + +**输入参数**: + +- **`compose_file`**(必填):`docker-compose` 文件路径,相对于当前工作目录或 `compose_dir`。 +- **`compose_dir`**(可选,默认使用 `compose_file` 所在目录):执行 `docker compose` 时的工作目录。 +- **`startup_wait`**(可选,默认 `0`):`up -d` 后额外等待的秒数,用于等待服务完成初始化。 + +**行为**: + +1. 计算 `compose_file` 与工作目录。 +2. 执行 `docker compose -f down` 停止旧服务(失败不终止)。 +3. 执行 `docker compose -f up -d` 启动服务。 +4. 可选地等待 `startup_wait` 秒。 +5. 执行 `docker compose ps` 并检查是否有 `exited` 状态的服务,若有则输出日志并失败。 + +**依赖环境**: + +- Runner 上需要安装 `docker` 与 `docker compose`,并安装 `jq`(用于解析 `docker compose ps --format json`)。 + +--- + +## `api-test` — API `/health` 健康检查(完整 URL) + +**路径**:`actions/api-test/action.yml` +**作用**:对一组后端服务的 `/health` 接口做健康检查。 +**约定**:输入的是 **不包含 `/health` 的完整 URL**,action 内部会自动拼接 `/health`。 + +**示例**(节选自 `broccoli-offline/.gitea/workflows/e2e-tests.yml`): + +```yaml +- name: Run offline API health tests + uses: https://git.aiot.ml/Obscura/common-workflows/actions/api-test@main + with: + urls: | + http://127.0.0.1:5710 + http://127.0.0.1:5711 + http://127.0.0.1:5712 + +``` + +**输入参数**: + +- **`urls`**(必填):多行字符串,每行一个完整 URL(不含 `/health`),例如: + - `http://127.0.0.1:5800` + - `https://service-name` + +**行为**: + +- 对每一行 URL: + - 去掉末尾 `/`,拼接 `/health`; + - 最多重试 10 次 `curl -fsS /health`,每次失败后等待 5 秒; + - 若所有重试均失败,则记录到失败列表。 +- 如果任意服务失败,整个步骤 `exit 1`。 + +**依赖环境**: + +- Runner 上需要安装 `curl`。 + +--- + +## `frontend-test` — 前端入口可访问性检查(完整 URL) + +**路径**:`actions/frontend-test/action.yml` +**作用**:对一组前端入口 URL 做可访问性检查(通常是首页)。 + +**示例**(节选自 `broccoli-offline/.gitea/workflows/e2e-tests.yml`): + +```yaml +- name: Run offline frontend availability tests + uses: https://git.aiot.ml/Obscura/common-workflows/actions/frontend-test@main + with: + urls: | + http://127.0.0.1:5373 +``` + +**输入参数**: + +- **`urls`**(必填):多行字符串,每行一个完整 URL,例如: + - `http://127.0.0.1:3000` + - `https://frontend-service` + +**行为**: + +- 对每一行 URL: + - 最多重试 10 次 `curl -fsS `,每次失败后等待 5 秒; + - 任意一次成功则认为该前端通过,否则记录失败。 +- 如果有任意 URL 访问失败,整个步骤 `exit 1`。 + +**依赖环境**: + +- Runner 上需要安装 `curl`。 + + diff --git a/actions/README.md b/actions/README.md new file mode 100644 index 0000000..86ae396 --- /dev/null +++ b/actions/README.md @@ -0,0 +1,185 @@ +## 通用复用 Actions 说明(`common-workflows/actions`) + +本目录下包含了一组可在各个仓库中复用的 **Gitea / GitHub composite actions**,统一托管在: + +- `https://git.aiot.ml/Obscura/common-workflows` + +在具体仓库的 workflow 中,通过 `uses` 引用,例如: + +```yaml +uses: https://git.aiot.ml/Obscura/common-workflows/actions/@main +``` + +目前提供的通用 action 如下: + +- **build-images**:构建并验证 Docker 镜像 +- **deploy-docker**:使用 `docker compose` 部署服务 +- **api-test**:对一组后端 API 的 `/health` 做健康检查 +- **frontend-test**:对一组前端入口 URL 做可访问性检查 + +下面分别说明用法。 + +--- + +## `build-images` — Docker 镜像构建 + +**路径**:`actions/build-images/action.yml` +**作用**:在 Runner 上本地执行 `docker build`,并对生成的镜像做简单校验与清理悬空镜像。 + +**引用示例**(节选自 `broccoli-offline`): + +```yaml +- name: Build Docker image + uses: https://git.aiot.ml/Obscura/common-workflows/actions/build-images@main + with: + dockerfile: 'Dockerfile.api' + context: '.' + image_name: 'broccoli-api' + image_tag: 'v3.2.2' + enable_cache: 'true' + # build_args: | + # HTTP_PROXY=http://... + # HTTPS_PROXY=http://... +``` + +**输入参数**: + +- **`dockerfile`**(必填):Dockerfile 路径,相对于仓库根目录,例如 `Dockerfile`, `Dockerfile.api`。 +- **`context`**(可选,默认 `.`):构建上下文目录,相对于仓库根目录。 +- **`image_name`**(必填):镜像名称,不含 tag,例如 `broccoli-api`。 +- **`image_tag`**(必填):镜像 tag / 版本,例如 `v1.0.0`、`latest`。 +- **`enable_cache`**(可选,默认 `'true'`):是否启用构建缓存(当前主要依赖 Runner 本地 docker layer cache)。 +- **`build_args`**(可选):Docker build 参数,每行一个,格式 `KEY=VALUE`,会被拼接为 `--build-arg KEY=VALUE`。 + +**依赖环境**: + +- Runner 上需要已安装 `docker`,并有构建镜像的权限。 + +--- + +## `deploy-docker` — Docker Compose 部署 + +**路径**:`actions/deploy-docker/action.yml` +**作用**:在已经 checkout 好代码的前提下,使用 `docker compose` 停止旧服务并重新部署,最后校验服务状态。 + +**典型用法**(示例): + +```yaml +- name: Checkout code + uses: https://git.aiot.ml/Obscura/checkout@v4 + with: + ref: offline + sparse-checkout: | + .gitea/workflows/deploy-docker.yml + config/ + docker-compose-offline.yml + sparse-checkout-cone-mode: false + +- name: Deploy with reusable action + uses: https://git.aiot.ml/Obscura/common-workflows/actions/deploy-docker@main + with: + compose_file: 'docker-compose-offline.yml' + # compose_dir: 'config' # 可选:指定工作目录 + # startup_wait: '30' # 可选:启动后等待 30s 再校验 +``` + +**输入参数**: + +- **`compose_file`**(必填):`docker-compose` 文件路径,相对于当前工作目录或 `compose_dir`。 +- **`compose_dir`**(可选,默认使用 `compose_file` 所在目录):执行 `docker compose` 时的工作目录。 +- **`startup_wait`**(可选,默认 `0`):`up -d` 后额外等待的秒数,用于等待服务完成初始化。 + +**行为**: + +1. 计算 `compose_file` 与工作目录。 +2. 执行 `docker compose -f down` 停止旧服务(失败不终止)。 +3. 执行 `docker compose -f up -d` 启动服务。 +4. 可选地等待 `startup_wait` 秒。 +5. 执行 `docker compose ps` 并检查是否有 `exited` 状态的服务,若有则输出日志并失败。 + +**依赖环境**: + +- Runner 上需要安装 `docker` 与 `docker compose`,并安装 `jq`(用于解析 `docker compose ps --format json`)。 + +--- + +## `api-test` — API `/health` 健康检查(完整 URL) + +**路径**:`actions/api-test/action.yml` +**作用**:对一组后端服务的 `/health` 接口做健康检查。 +**约定**:输入的是 **不包含 `/health` 的完整 URL**,action 内部会自动拼接 `/health`。 + +**示例**(节选自 `broccoli-offline/.gitea/workflows/e2e-tests.yml`): + +```yaml +- name: Run offline API health tests + uses: https://git.aiot.ml/Obscura/common-workflows/actions/api-test@main + with: + urls: | + http://10.0.0.145:5710 + http://10.0.0.145:5711 + http://10.0.0.145:5712 + http://10.0.0.145:5713 + http://10.0.0.145:5714 + http://10.0.0.145:5715 + http://10.0.0.145:5716 + http://10.0.0.145:5717 + http://10.0.0.145:5718 + http://10.0.0.145:5719 + http://10.0.0.145:5720 + http://10.0.0.145:5721 +``` + +**输入参数**: + +- **`urls`**(必填):多行字符串,每行一个完整 URL(不含 `/health`),例如: + - `http://127.0.0.1:5800` + - `https://service-name` + +**行为**: + +- 对每一行 URL: + - 去掉末尾 `/`,拼接 `/health`; + - 最多重试 10 次 `curl -fsS /health`,每次失败后等待 5 秒; + - 若所有重试均失败,则记录到失败列表。 +- 如果任意服务失败,整个步骤 `exit 1`。 + +**依赖环境**: + +- Runner 上需要安装 `curl`。 + +--- + +## `frontend-test` — 前端入口可访问性检查(完整 URL) + +**路径**:`actions/frontend-test/action.yml` +**作用**:对一组前端入口 URL 做可访问性检查(通常是首页)。 + +**示例**(节选自 `broccoli-offline/.gitea/workflows/e2e-tests.yml`): + +```yaml +- name: Run offline frontend availability tests + uses: https://git.aiot.ml/Obscura/common-workflows/actions/frontend-test@main + with: + urls: | + http://10.0.0.145:5373 +``` + +**输入参数**: + +- **`urls`**(必填):多行字符串,每行一个完整 URL,例如: + - `http://127.0.0.1:3000` + - `https://frontend-service` + +**行为**: + +- 对每一行 URL: + - 最多重试 10 次 `curl -fsS `,每次失败后等待 5 秒; + - 任意一次成功则认为该前端通过,否则记录失败。 +- 如果有任意 URL 访问失败,整个步骤 `exit 1`。 + +**依赖环境**: + +- Runner 上需要安装 `curl`。 + + diff --git a/actions/api-test/action.yml b/actions/api-test/action.yml new file mode 100644 index 0000000..f86de29 --- /dev/null +++ b/actions/api-test/action.yml @@ -0,0 +1,74 @@ +name: 'Reusable API Health Test' +description: '通用的 API /health 检查逻辑,可在各仓库中复用,支持传入完整 URL(不含 /health)' + +inputs: + urls: + description: '要检查的服务 URL 列表,每行一个完整 URL(不包含 /health),例如 http://127.0.0.1:5800 或 https://service-name' + required: true + +runs: + using: 'composite' + steps: + - name: Check API services + shell: bash + env: + URLS: ${{ inputs.urls }} + run: | + echo "====== 开始检查各 API 服务 /health ======" + + failed_services="" + api_failed=0 + + # 按行读取 URL,每行是一个完整的基础 URL(不包含 /health) + while IFS= read -r url; do + # 跳过空行 + if [ -z "$url" ]; then + continue + fi + + # 去掉末尾的 /,避免出现 //health + url_no_slash="${url%/}" + health_url="${url_no_slash}/health" + + echo "" + echo "------ 检查服务 ${health_url} ------" + + success=0 + for i in {1..10}; do + if curl -fsS "${health_url}" > /dev/null; then + success=1 + break + fi + echo "第 ${i} 次重试,等待 5 秒..." + sleep 5 + done + + if [ "${success}" -ne 1 ]; then + echo "✗ 服务 ${health_url} /health 检查失败" + failed_services="${failed_services} ${health_url}" + else + echo "✓ 服务 ${health_url} /health 正常" + fi + done <<< "$URLS" + + if [ -n "${failed_services}" ]; then + echo "" + echo "✗ 以下服务健康检查失败:" + for s in ${failed_services}; do + echo " - ${s}" + done + api_failed=1 + else + echo "" + echo "====== 所有 API 服务健康检查通过 ======" + fi + + if [ "${api_failed}" -ne 0 ]; then + echo "" + echo "✗ API /health 检查存在失败,详见上方日志" + exit 1 + fi + + echo "" + echo "✓ 所有 API /health 检查通过" + diff --git a/actions/frontend-test/action.yml b/actions/frontend-test/action.yml new file mode 100644 index 0000000..713af0f --- /dev/null +++ b/actions/frontend-test/action.yml @@ -0,0 +1,56 @@ +name: 'Reusable Frontend Availability Test' +description: '通用的前端首页可访问性检查逻辑,可在各仓库中复用,支持传入完整 URL' + +inputs: + urls: + description: '要检查的前端 URL 列表,每行一个完整 URL,例如 http://127.0.0.1:5373 或 https://frontend-service' + required: true + +runs: + using: 'composite' + steps: + - name: Check frontend + shell: bash + env: + URLS: ${{ inputs.urls }} + run: | + echo "====== 开始检查前端首页可访问性 ======" + + frontend_failed=0 + + # 按行读取 URL,每行是一个完整的前端访问地址 + while IFS= read -r url; do + # 跳过空行 + if [ -z "$url" ]; then + continue + fi + + echo "" + echo "------ 检查前端 ${url} ------" + + success=0 + for i in {1..10}; do + if curl -fsS "${url}" > /dev/null; then + echo "✓ 前端首页可访问: ${url}" + success=1 + break + fi + echo "第 ${i} 次重试,等待 5 秒..." + sleep 5 + done + + if [ "${success}" -ne 1 ]; then + echo "✗ 前端首页访问失败: ${url}" + frontend_failed=1 + fi + done <<< "$URLS" + + if [ "${frontend_failed}" -ne 0 ]; then + echo "" + echo "✗ 前端检查存在失败,详见上方日志" + exit 1 + fi + + echo "" + echo "✓ 所有前端检查通过" +