3 Commits
Author SHA1 Message Date
Qiufeng 3e0adfc0bf docs: add project README
Signed Release / release (push) Failing after 20s
2026-08-04 05:58:48 +08:00
Qiufeng 882d8f559c fix: support piped installer execution 2026-08-04 00:33:16 +08:00
Qiufeng a604460542 ci: use scoped Gitea release token 2026-08-04 00:06:55 +08:00
5 changed files with 460 additions and 7 deletions
+7 -3
View File
@@ -5,14 +5,18 @@ on:
tags: tags:
- "v*" - "v*"
permissions:
contents: read
releases: write
jobs: jobs:
release: release:
runs-on: ubuntu-latest runs-on: ubuntu-latest
env: env:
GITEA_BASE_URL: ${{ vars.GITEA_BASE_URL }} GITEA_BASE_URL: ${{ github.server_url }}
GITEA_REPOSITORY: ${{ github.repository }} GITEA_REPOSITORY: ${{ github.repository }}
GITEA_ALLOW_INSECURE_HTTP: ${{ vars.GITEA_ALLOW_INSECURE_HTTP }} GITEA_ALLOW_INSECURE_HTTP: ${{ vars.GITEA_ALLOW_INSECURE_HTTP }}
GITEA_TOKEN: ${{ secrets.RELEASE_GITEA_TOKEN }} GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
RELEASE_PRIVATE_KEY_B64: ${{ secrets.RELEASE_PRIVATE_KEY_B64 }} RELEASE_PRIVATE_KEY_B64: ${{ secrets.RELEASE_PRIVATE_KEY_B64 }}
NODE_OPTIONS: --max-old-space-size=8192 NODE_OPTIONS: --max-old-space-size=8192
steps: steps:
@@ -44,7 +48,7 @@ jobs:
esac esac
[[ "$repository" =~ ^[^/[:space:]]+/[^/[:space:]]+$ ]] \ [[ "$repository" =~ ^[^/[:space:]]+/[^/[:space:]]+$ ]] \
|| { printf 'invalid GITEA_REPOSITORY\n' >&2; exit 1; } || { printf 'invalid GITEA_REPOSITORY\n' >&2; exit 1; }
[[ -n "${GITEA_TOKEN:-}" ]] || { printf 'missing RELEASE_GITEA_TOKEN secret\n' >&2; exit 1; } [[ -n "${GITEA_TOKEN:-}" ]] || { printf 'missing built-in GITEA_TOKEN\n' >&2; exit 1; }
[[ -n "${RELEASE_PRIVATE_KEY_B64:-}" ]] \ [[ -n "${RELEASE_PRIVATE_KEY_B64:-}" ]] \
|| { printf 'missing RELEASE_PRIVATE_KEY_B64 secret\n' >&2; exit 1; } || { printf 'missing RELEASE_PRIVATE_KEY_B64 secret\n' >&2; exit 1; }
+431
View File
@@ -0,0 +1,431 @@
# 凯迪 ERP + OA 一体化平台
凯迪 ERP + OA 是面向企业内部运营的综合业务平台,覆盖组织协同、审批、合同、采购、库存、财务、人力、项目、制造、运营、档案、审计和系统管理等工作域。前端采用 Vue 3 + TypeScript + Element Plus,后端采用 Spring Boot 3.2 + Spring Data JPA。
> 生产部署固定使用 PostgreSQL 15 或更高版本,不使用 Docker。源码目录保留的 SQLite 配置只用于本地开发和测试;正式 Release JAR 会排除 SQLite 驱动和社区方言。
## 项目现状
- 业务范围覆盖 29 个机构/部门,包含约 700 个 Vue 业务页面和大规模 REST/JPA 模型。
- 当前有效后端位于 `oa-backend/`
- 当前有效前端位于 `ofbiz-framework/plugins/modern-ui/app/`;其上层旧 OFBiz 运行框架已经弃用。
- 前端构建产物直接写入后端 `src/main/resources/static/`,由同一个 Spring Boot JAR 提供页面与 `/api/oa/*` API。
- 已实现非 Docker 安装、PostgreSQL Flyway 迁移、Gitea Release 更新、Ed25519/SHA-256 校验、服务重启、健康检查和自动回滚。
- 业务功能和外部系统接入仍需按项目需求验收;技术部署链路通过不等于可以跳过生产安全、备份和数据迁移评审。
## 两种运行模式
| 模式 | 数据库 | 用途 | 关键约束 |
|---|---|---|---|
| 源码开发 | SQLite 或 PostgreSQL | 本机开发、界面调试、自动化测试 | SQLite 仅为零配置开发选项,不代表在线环境 |
| 正式安装 | PostgreSQL 15+ | 服务器部署、在线更新 | 使用签名 Release、systemd/launchd,生产 JAR 不含 SQLite |
## 架构
```text
Browser / Mobile Web
|
v
Spring Boot 3.2
|- Vue 3 static application
|- /api/oa/* REST API
|- authentication and permission gates
|- system update administration API
|
v
PostgreSQL 15+ <--- Flyway migrations
Administrator -> System Update UI -> erp-update helper
|- Gitea Release API
|- Ed25519 + SHA-256 verification
|- atomic current symlink switch
`- health check and rollback
```
## 仓库结构
```text
.
|- README.md
|- run.command # macOS 本地预览与 ngrok 启动器
|- install.sh # 非 Docker 一键安装器
|- distribution/
| |- bin/erp-run # 正式环境应用启动器
| `- bin/erp-update # 下载、校验、切换和回滚助手
|- scripts/package-release.sh # PostgreSQL-only Release 打包与签名
|- .gitea/workflows/release.yml # v* tag 发布流水线
|- oa-backend/ # Spring Boot 后端
| `- src/main/resources/
| |- application.yml # 源码开发默认配置
| |- application-postgres.yml # 正式 PostgreSQL profile
| `- db/migration/postgresql/ # Flyway 迁移
|- ofbiz-framework/plugins/modern-ui/app/
| |- src/data/oaModules.ts # 导航与路由数据源
| `- src/oa/pages/ # Vue 业务页面
|- tests/ # 启动器和 Release 脚本测试
|- docs/ # 部署、设计和实现文档
`- requirements/ # 原始需求与合规审计材料
```
## 本地构建
克隆仓库并进入开发分支:
```bash
git clone git@38.76.196.225:awaioi/ERP.git
cd ERP
git switch dev
git config core.hooksPath .githooks
```
### 环境要求
- Java 17 或更高版本
- Node.js 和 npm
- Python 3、curl、lsof
- 使用公网预览时需要已登录并配置好的 ngrok 3
- PostgreSQL 模式需要 PostgreSQL 15+ 以及可连接的数据库账号
### 1. 构建前端
```bash
cd ofbiz-framework/plugins/modern-ui/app
npm ci
NODE_OPTIONS=--max-old-space-size=8192 npm run build
```
Vite 会清空并重新生成 `oa-backend/src/main/resources/static/`。修改前端后必须先执行前端构建,再执行 `bootJar`,否则 JAR 中仍是旧页面。
### 2. 构建后端
开发构建保留 SQLite 运行库:
```bash
cd oa-backend
./gradlew test
./gradlew bootJar
```
PostgreSQL-only 正式构建:
```bash
cd oa-backend
./gradlew clean bootJar -PreleaseVersion=0.2.0 -PproductionBuild=true
```
正式 JAR 必须包含 PostgreSQL 驱动,并且不得包含 `sqlite-jdbc``hibernate-community-dialects`
## 一键启动本地预览
根目录的 `run.command` 会检查环境、启动或复用 ERP 后端、启动或复用固定 ngrok 隧道、验证本地登录和公网页面,然后打开浏览器。它不会安装 PostgreSQL、构建前端或生成 JAR。
```bash
chmod +x run.command
./run.command
```
macOS 也可以在 Finder 中双击 `run.command`。终端保持运行用于监控服务,按 `Ctrl+C` 只会停止本次启动器自己创建的进程,不会批量终止其他 Java 进程。
常用覆盖项:
| 环境变量 | 默认值 | 说明 |
|---|---|---|
| `ERP_RUN_BACKEND_PORT` | `8091` | 本地 Spring Boot 端口 |
| `ERP_RUN_JAR_PATH` | 最新的 `oa-backend-*.jar` | 指定要运行的 JAR |
| `ERP_RUN_PROFILE` | 当前环境的 Spring profile | 正式环境设为 `postgres` |
| `ERP_RUN_CONFIG_FILE` | 空 | 读取安装器生成的 `erp.env` |
| `ERP_RUN_PUBLIC_URL` | 固定 ngrok 域名 | 公网预览地址 |
| `ERP_RUN_NGROK_API_PORT` | `4040` | ngrok 本地管理端口 |
| `ERP_RUN_NO_OPEN` | `0` | 设为 `1` 时不自动打开浏览器 |
直接以 PostgreSQL profile 运行源码构建时:
```bash
export SPRING_PROFILES_ACTIVE=postgres
export OA_DB_URL='jdbc:postgresql://127.0.0.1:5432/kaidi_erp?sslmode=disable'
export OA_DB_USERNAME='kaidi_erp'
export OA_DB_PASSWORD='replace-with-a-strong-password'
./run.command
```
默认本地地址为 `http://127.0.0.1:8091`。开发预览固定域名为:
```text
https://resonant-elated-launder.ngrok-free.dev/
```
演示环境初始账号为 `admin / 123456`。任何共享或生产环境都必须立即更换默认密码,并限制公网访问。
## 非 Docker 正式安装
安装器支持 64 位 Linux `amd64/arm64` 和 macOS `arm64/amd64`。Linux 安装需要 rootmacOS 不应使用 sudo 启动 LaunchAgent。正式安装前必须已有包含下列三个资产的 Gitea Release
- `kaidi-erp-<version>.tar.gz`
- `SHA256SUMS`
- `SHA256SUMS.sig`
### Linux 使用已有 PostgreSQL
生产环境应先为 Gitea 配置 HTTPS
```bash
curl -fsSL https://git.example.com/awaioi/ERP/raw/branch/main/install.sh \
| sudo -E bash -s -- \
--gitea-url https://git.example.com \
--repository awaioi/ERP
```
安装器会从 `/dev/tty` 询问数据库地址、端口、数据库名、账号、密码和 SSL 模式,校验 PostgreSQL 版本及连接后再下载 Release。
### apt 系 Linux 自动创建本机 PostgreSQL
```bash
curl -fsSL https://git.example.com/awaioi/ERP/raw/branch/main/install.sh \
| sudo -E bash -s -- \
--db-mode local \
--gitea-url https://git.example.com \
--repository awaioi/ERP
```
`--db-mode local` 当前只支持使用 apt 和 systemd 的 Linux。其他 Linux 发行版应先准备 PostgreSQL,再使用默认的 `existing` 模式。
### macOS
macOS 需要 Homebrew,并使用已有 PostgreSQL
```bash
curl -fsSL https://git.example.com/awaioi/ERP/raw/branch/main/install.sh \
| bash -s -- \
--gitea-url https://git.example.com \
--repository awaioi/ERP
```
### 无人值守安装
```bash
export ERP_DB_HOST=127.0.0.1
export ERP_DB_PORT=5432
export ERP_DB_NAME=kaidi_erp
export ERP_DB_USER=kaidi_erp
export ERP_DB_PASSWORD='replace-with-a-strong-password'
export ERP_DB_SSLMODE=require
curl -fsSL https://git.example.com/awaioi/ERP/raw/branch/main/install.sh \
| sudo -E bash -s -- \
--non-interactive \
--gitea-url https://git.example.com \
--repository awaioi/ERP
```
### 当前 HTTP 测试服务器
当前 Gitea 地址 `http://38.76.196.225:10099` 只允许用于开发验收:
```bash
curl -fsSL http://38.76.196.225:10099/awaioi/ERP/raw/branch/main/install.sh \
| sudo -E bash -s -- \
--gitea-url http://38.76.196.225:10099 \
--repository awaioi/ERP \
--allow-insecure
```
只有首个 Release 发布后这条命令才可下载安装包。HTTP 会暴露请求、Release 元数据和可能使用的访问令牌,不得作为生产方案。
### 正式安装目录
Linux 默认路径:
| 内容 | 路径 |
|---|---|
| 程序和版本目录 | `/opt/kaidi-erp` |
| 环境配置 | `/etc/kaidi-erp/erp.env` |
| 更新状态 | `/var/lib/kaidi-erp/update-state.json` |
| systemd 服务 | `kaidi-erp.service` |
使用 `--no-service` 只安装文件,不注册 systemd/launchd,同时会默认设置 `OA_UPDATE_ENABLED=false`。在线更新需要能在 Java 进程退出后自动拉起新版本的进程管理器。
## 在线更新与回滚
管理员登录后进入:
```text
应用定制平台 -> 系统更新
```
对应前端路由为 `/appdev/update`,后端 API 为 `/api/oa/system-update/*`。更新过程如下:
1. 从 Gitea 读取 stable channel 的最新 Release。
2. 下载归档、`SHA256SUMS` 和 Ed25519 签名。
3. 先验证签名和 SHA-256,再拒绝路径穿越、绝对路径、符号链接、硬链接和结构不完整的归档。
4. 校验 `manifest.json` 中的版本、`database=postgresql``rollbackCompatible=true`
5. 可选执行 `pg_dump`,将新版本写入独立目录。
6. 原子切换 `current` 符号链接,并停止旧 Java 进程。
7. systemd/launchd 拉起新版本,更新助手等待新的 PID 和 `/api/oa/health`
8. 新版本不健康时切回上一链接,终止故障进程并再次验证旧版本健康状态。
同一安装目录使用操作系统文件锁,不能并发执行两个更新任务。也可以手工触发:
```bash
/opt/kaidi-erp/current/bin/erp-update install 0.2.0
```
应用回滚不等于数据库回滚。包含不可逆 Flyway 迁移的版本必须先保证旧应用仍兼容新结构,并建议在安装配置中启用:
```bash
ERP_UPDATE_BACKUP_MODE=pg_dump
```
更新助手不会自动覆盖生产数据库。需要恢复数据库时,应由管理员确认后使用 `pg_restore`
## Gitea Release 发布
推送 `v*` tag 会触发 `.gitea/workflows/release.yml`。流水线会构建前端、生成 PostgreSQL-only JAR、打包、签名,并创建或更新对应 Gitea Release。
### Actions 前置配置
Gitea 1.27 仓库需要启用 Actions,并配置带 `ubuntu-latest` 标签的在线 Runner。Runner 必须提供:
- Java 17+
- Node.js 和 npm
- Python 3
- curl、tar
- OpenSSL 3,且支持 Ed25519
流水线使用 Gitea 内置短期 `GITEA_TOKEN`,权限限定为代码只读、当前仓库 Release 可写。仓库设置只需添加:
- Secret `RELEASE_PRIVATE_KEY_B64`
- 当前纯 HTTP 测试服务器额外添加 Variable `GITEA_ALLOW_INSECURE_HTTP=1`
签名私钥不得提交到 Git。生成 Secret 值:
```bash
base64 < ~/.config/kaidi-erp/release-signing-key.pem | tr -d '\n'
```
发布稳定版本:
```bash
git switch main
git pull --ff-only origin main
git tag -a v0.2.0 -m 'Kaidi ERP v0.2.0'
git push origin v0.2.0
```
发布完成后必须确认 Release 页面存在三个资产,并使用仓库中的 `distribution/release-public-key.pem` 验证签名。私钥与该公钥不匹配时打包脚本会直接失败。
本地手工生成签名资产:
```bash
ERP_RELEASE_PRIVATE_KEY_FILE="$HOME/.config/kaidi-erp/release-signing-key.pem" \
bash scripts/package-release.sh 0.2.0
```
## 配置参考
| 环境变量 | 说明 | 默认值 |
|---|---|---|
| `SPRING_PROFILES_ACTIVE` | 正式环境必须为 `postgres` | 源码默认 profile |
| `OA_DB_URL` | JDBC PostgreSQL URL | 正式环境必填 |
| `OA_DB_USERNAME` | PostgreSQL 用户 | 正式环境必填 |
| `OA_DB_PASSWORD` | PostgreSQL 密码 | 正式环境必填 |
| `OA_DB_POOL_MAX` | 最大连接池 | `20` |
| `OA_DB_POOL_MIN` | 最小空闲连接 | `2` |
| `OA_UPDATE_ENABLED` | 启用管理后台在线更新 | 安装服务时为 `true` |
| `OA_UPDATE_GITEA_BASE_URL` | Gitea 外部地址 | 正式安装时写入 |
| `OA_UPDATE_REPOSITORY` | Release 仓库 | `awaioi/ERP` |
| `OA_UPDATE_CHANNEL` | 更新通道 | `stable` |
| `OA_UPDATE_TOKEN` | 私有仓库下载令牌 | 空;公开仓库不需要 |
| `OA_UPDATE_ALLOW_INSECURE_HTTP` | 允许 HTTP 更新地址 | `false` |
| `ERP_UPDATE_BACKUP_MODE` | 更新前数据库备份 | `none`,可设 `pg_dump` |
| `ERP_UPDATE_HEALTH_TIMEOUT_SECONDS` | 新旧版本健康检查超时 | `120` |
| `ERP_UPDATE_HEALTH_POLL_SECONDS` | 健康轮询间隔 | `2` |
## 测试与验收
### 后端
```bash
cd oa-backend
./gradlew test
```
### 前端类型检查与构建
```bash
cd ofbiz-framework/plugins/modern-ui/app
npm ci
NODE_OPTIONS=--max-old-space-size=8192 npm run build
```
### 启动器与发布脚本
```bash
bash tests/run-command.test.sh
bash tests/release-scripts.test.sh
```
### 已运行服务的 OA 冒烟和集成测试
```bash
OA=http://127.0.0.1:8091 bash oa-smoke.sh
OA=http://127.0.0.1:8091 bash oa-itest.sh
```
测试脚本会创建业务测试数据,应使用测试数据库,不要直接对生产数据库运行写入型集成测试。
## 安全与运维注意事项
- 正式 Gitea、安装和更新流量必须使用 HTTPS;`--allow-insecure` 仅限隔离测试环境。
- `RELEASE_PRIVATE_KEY_B64` 和 PostgreSQL 密码不得写入 Git、日志或聊天记录。
- 新增返回金额、个人信息或机密汇总的 API 时,必须复核 `AuthInterceptor` 的敏感读取权限前缀。
- 金额字段和计算统一使用 `BigDecimal`,禁止使用 `double` 处理货币。
- 不要使用 `pkill java``killall java`;只停止明确属于本项目的 PID。
- 修改前端后始终先执行前端构建,再打包后端。
- 每次包含数据库结构变更的 Release 都必须新增 Flyway 迁移并做新库首次迁移、旧库升级和回滚兼容性验证。
- 默认演示账号只能用于开发环境,生产环境需要更换密码并实施最小权限、备份、监控和审计策略。
## 常见问题
### 安装器提示没有 Release
Gitea 仓库尚未发布首个可安装版本,或 Release 缺少三个必需资产。先检查 `/api/v1/repos/awaioi/ERP/releases/latest` 和 Release 页面。
### PostgreSQL profile 启动失败
确认 `SPRING_PROFILES_ACTIVE=postgres`,并检查 `OA_DB_URL``OA_DB_USERNAME``OA_DB_PASSWORD`。正式 profile 会执行 Flyway,再使用 Hibernate `ddl-auto=validate` 校验实体与数据库结构。
### `run.command` 提示端口被占用
启动器只会复用能够通过项目健康检查的监听进程。其他程序占用端口时不会被自动终止;请停止对应程序或设置新的 `ERP_RUN_BACKEND_PORT`
### ngrok 无法启动
运行 `ngrok config check` 确认配置有效,并检查固定域名是否属于当前 ngrok 账号。可以通过 `ERP_RUN_NGROK_BIN``ERP_RUN_NGROK_CONFIG``ERP_RUN_NGROK_API_PORT` 覆盖路径与端口。
### 签名验证失败
不要跳过验证。确认 Release 的三个资产来自同一次构建、`SHA256SUMS` 未被改写、签名私钥与 `distribution/release-public-key.pem` 匹配。
## Git 协作
- `dev` 是日常开发分支。
- `main` 只接收已经通过构建、测试、启动和冒烟验证的稳定提交。
- 新环境执行 `git config core.hooksPath .githooks` 启用仓库 hooks。
- 不要在脏工作区执行破坏性 reset;先确认哪些修改属于正在进行的开发。
远程仓库:
```text
git@38.76.196.225:awaioi/ERP.git
```
## 延伸文档
- [在线安装与更新](docs/online-install-and-update.md)
- [项目交接文档](go.md)
- [设计原则](DESIGN.md)
- [后端 API](oa-backend/API.md)
- [后端安全说明](oa-backend/SECURITY.md)
- [端点目录](go-endpoints.md)
- [实体目录](go-entities.md)
- [数据库目录](go-database.md)
+1 -3
View File
@@ -12,11 +12,9 @@ Kaidi ERP 的生产安装不依赖 Docker。安装器支持 64 位 Linux 和 mac
Gitea Actions runner 需要预装 Java 17 或更高版本、Node.js/npm、Python 3、tar、curl 和 OpenSSL 3。 Gitea Actions runner 需要预装 Java 17 或更高版本、Node.js/npm、Python 3、tar、curl 和 OpenSSL 3。
仓库 Actions 设置创建: 流水线使用 Gitea 1.27 提供的短期 `GITEA_TOKEN`,权限限定为代码只读、当前仓库 Release 可写,不需要创建个人访问令牌。仓库 Actions 设置只需创建:
- Secret `RELEASE_GITEA_TOKEN`:具备当前仓库 Release 写权限的 Gitea token。
- Secret `RELEASE_PRIVATE_KEY_B64`Ed25519 私钥的单行 Base64 内容。 - Secret `RELEASE_PRIVATE_KEY_B64`Ed25519 私钥的单行 Base64 内容。
- Variable `GITEA_BASE_URL`:生产 Gitea 的 HTTPS 外部地址。
- Variable `GITEA_ALLOW_INSECURE_HTTP`:生产环境不要设置;当前 HTTP 测试服务器必须显式设为 `1` - Variable `GITEA_ALLOW_INSECURE_HTTP`:生产环境不要设置;当前 HTTP 测试服务器必须显式设为 `1`
本机现有签名私钥位于 `~/.config/kaidi-erp/release-signing-key.pem`,不得提交到 Git。macOS 可用以下命令生成 Secret 值: 本机现有签名私钥位于 `~/.config/kaidi-erp/release-signing-key.pem`,不得提交到 Git。macOS 可用以下命令生成 Secret 值:
+1 -1
View File
@@ -520,6 +520,6 @@ main() {
say "Local URL: http://127.0.0.1:${ERP_SERVER_PORT:-8091}" say "Local URL: http://127.0.0.1:${ERP_SERVER_PORT:-8091}"
} }
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then if [[ "${BASH_SOURCE[0]:-$0}" == "$0" ]]; then
main "$@" main "$@"
fi fi
+20
View File
@@ -488,6 +488,24 @@ test_no_service_install_disables_online_update() (
grep -Fqx 'OA_UPDATE_ENABLED=false' "$CONFIG_ROOT/erp.env" grep -Fqx 'OA_UPDATE_ENABLED=false' "$CONFIG_ROOT/erp.env"
) )
test_release_workflow_uses_scoped_job_token() (
local workflow="$PROJECT_ROOT/.gitea/workflows/release.yml"
grep -Fqx ' contents: read' "$workflow"
grep -Fqx ' releases: write' "$workflow"
grep -Fq 'GITEA_BASE_URL: ${{ github.server_url }}' "$workflow"
grep -Fq 'GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}' "$workflow"
! grep -Fq 'RELEASE_GITEA_TOKEN' "$workflow"
! grep -Fq 'vars.GITEA_BASE_URL' "$workflow"
)
test_installer_runs_when_piped_to_bash() (
local output status=0
output="$(ERP_GITEA_BASE_URL= bash -s -- --no-service < "$PROJECT_ROOT/install.sh" 2>&1)" || status=$?
[[ "$status" -ne 0 ]]
[[ "$output" == *'[ERP Install] ERROR:'* ]]
[[ "$output" != *'BASH_SOURCE'* ]]
)
run_test 'erp-run preserves Java option arguments' test_erp_run_preserves_java_option_arguments run_test 'erp-run preserves Java option arguments' test_erp_run_preserves_java_option_arguments
run_test 'installer accepts a correctly signed archive' test_installer_verifies_signed_safe_archive run_test 'installer accepts a correctly signed archive' test_installer_verifies_signed_safe_archive
run_test 'installer rejects symlinks in release archives' test_installer_rejects_archive_symlinks run_test 'installer rejects symlinks in release archives' test_installer_rejects_archive_symlinks
@@ -499,6 +517,8 @@ run_test 'update helper rejects a concurrent process' test_update_lock_rejects_c
run_test 'noninteractive install reports a missing database password' test_noninteractive_installer_reports_missing_password run_test 'noninteractive install reports a missing database password' test_noninteractive_installer_reports_missing_password
run_test 'installer requires an explicit Gitea URL' test_installer_requires_explicit_gitea_url run_test 'installer requires an explicit Gitea URL' test_installer_requires_explicit_gitea_url
run_test 'no-service install disables online update' test_no_service_install_disables_online_update run_test 'no-service install disables online update' test_no_service_install_disables_online_update
run_test 'release workflow uses the scoped Gitea job token' test_release_workflow_uses_scoped_job_token
run_test 'installer starts correctly when piped to bash' test_installer_runs_when_piped_to_bash
printf 'RESULT pass=%s fail=%s\n' "$PASS" "$FAIL" printf 'RESULT pass=%s fail=%s\n' "$PASS" "$FAIL"
[[ "$FAIL" -eq 0 ]] [[ "$FAIL" -eq 0 ]]