docs: add Gitea-based Docker update workflow
This commit is contained in:
+39
-1
@@ -145,7 +145,45 @@ docker compose restart xiaobai-review
|
|||||||
docker compose down
|
docker compose down
|
||||||
```
|
```
|
||||||
|
|
||||||
更新程序:
|
### 使用 Gitea 更新程序(推荐)
|
||||||
|
|
||||||
|
代码仓库为:
|
||||||
|
|
||||||
|
```text
|
||||||
|
http://192.168.200.36:3200/leefer/xiaobaifupan.git
|
||||||
|
```
|
||||||
|
|
||||||
|
首次在服务器部署代码时,可以直接克隆到目标目录:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo mkdir -p /opt/xiaobai-review
|
||||||
|
sudo chown "$USER":"$USER" /opt/xiaobai-review
|
||||||
|
git clone http://192.168.200.36:3200/leefer/xiaobaifupan.git /opt/xiaobai-review
|
||||||
|
cd /opt/xiaobai-review
|
||||||
|
```
|
||||||
|
|
||||||
|
私有仓库会提示输入 Gitea 用户名和密码或访问令牌。不要把密码写入仓库 URL、
|
||||||
|
`compose.yaml` 或脚本。然后把原 `.env` 与 `data/` 放回该目录;这两项已被 Git
|
||||||
|
忽略,后续拉取代码不会覆盖数据库与密钥。
|
||||||
|
|
||||||
|
每次更新前先创建 SQLite 一致性备份,再拉取并重建容器:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /opt/xiaobai-review
|
||||||
|
docker compose exec -T xiaobai-review python -c "import sqlite3; s=sqlite3.connect('/app/data/review.db'); d=sqlite3.connect('/app/data/review-before-update.db'); s.backup(d); d.close(); s.close()"
|
||||||
|
git pull --ff-only origin main
|
||||||
|
docker compose up -d --build
|
||||||
|
docker compose ps
|
||||||
|
curl --fail http://127.0.0.1:8765/api/health
|
||||||
|
```
|
||||||
|
|
||||||
|
`docker compose up -d --build` 会原地替换应用容器,不删除宿主机的 `data` 目录。
|
||||||
|
数据库迁移会在新容器启动时自动执行。若 `git pull --ff-only` 提示本地代码有修改,
|
||||||
|
先用 `git status` 查明原因,不要用强制重置覆盖 `.env` 或 `data`。
|
||||||
|
|
||||||
|
### 不使用 Git 时更新
|
||||||
|
|
||||||
|
重新上传代码后执行:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker compose down
|
docker compose down
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
|
||||||
|
|
||||||
|
class DeploymentContractTests(unittest.TestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls) -> None:
|
||||||
|
cls.compose = (ROOT / "compose.yaml").read_text(encoding="utf-8")
|
||||||
|
cls.dockerfile = (ROOT / "Dockerfile").read_text(encoding="utf-8")
|
||||||
|
cls.dockerignore = (ROOT / ".dockerignore").read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
def test_compose_exposes_only_requested_lan_port(self):
|
||||||
|
self.assertIn('"0.0.0.0:8765:8765/tcp"', self.compose)
|
||||||
|
self.assertIn("read_only: true", self.compose)
|
||||||
|
self.assertIn("target: /app/data", self.compose)
|
||||||
|
self.assertIn("no-new-privileges:true", self.compose)
|
||||||
|
|
||||||
|
def test_image_runs_as_non_root_with_healthcheck(self):
|
||||||
|
self.assertIn("USER xiaobai", self.dockerfile)
|
||||||
|
self.assertIn("HEALTHCHECK", self.dockerfile)
|
||||||
|
self.assertIn('"--host", "0.0.0.0", "--port", "8765"', self.dockerfile)
|
||||||
|
|
||||||
|
def test_secrets_and_runtime_data_are_not_copied_into_image(self):
|
||||||
|
for pattern in (".env", "data/*.db", "data/*.db-wal", "data/*.db-shm"):
|
||||||
|
self.assertIn(pattern, self.dockerignore)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Reference in New Issue
Block a user