ci: release mai-bot without external image pulls

This commit is contained in:
LoveLosita
2026-05-11 09:40:54 +08:00
parent 02490c5fa1
commit 7d2a3599eb
3 changed files with 59 additions and 39 deletions

View File

@@ -0,0 +1,14 @@
ARG MAIBOT_BASE_IMAGE=maibot-offline:latest
FROM ${MAIBOT_BASE_IMAGE}
WORKDIR /MaiMBot
RUN find /MaiMBot -mindepth 1 -maxdepth 1 ! -name '.venv' -exec rm -rf {} +
COPY . .
RUN chmod +x deploy/server-maibot/docker-entrypoint.offline.sh
EXPOSE 8001
ENTRYPOINT ["./deploy/server-maibot/docker-entrypoint.offline.sh"]

View File

@@ -3,6 +3,7 @@ Release archive directory: /srv/maibot/releases
Repo-managed deployment files: Repo-managed deployment files:
- deploy/server-maibot/Dockerfile.offline - deploy/server-maibot/Dockerfile.offline
- deploy/server-maibot/Dockerfile.release
- deploy/server-maibot/docker-entrypoint.offline.sh - deploy/server-maibot/docker-entrypoint.offline.sh
- deploy/server-maibot/docker-compose.server.yml - deploy/server-maibot/docker-compose.server.yml
- deploy/server-maibot/activate-release.sh - deploy/server-maibot/activate-release.sh
@@ -23,10 +24,18 @@ Current pipeline mode:
- single-host release on the repo-level `build-host` runner - single-host release on the repo-level `build-host` runner
- clones from local Gitea HTTP on `127.0.0.1:3000` - clones from local Gitea HTTP on `127.0.0.1:3000`
- stages source into `/srv/maibot/releases/<commit>` - stages source into `/srv/maibot/releases/<commit>`
- activates it into `/root/maibot-offline` - builds `maibot-offline:<commit>` from the staged release using local base image `maibot-offline:latest`
- tags the same image back to `maibot-offline:latest`
- deploys from `/root/maibot-offline` with `docker compose up -d`
Optional environment overrides for the workflow runtime: Optional environment overrides for the workflow runtime:
- `MAIBOT_RELEASE_ROOT` - `MAIBOT_RELEASE_ROOT`
- `MAIBOT_RUNTIME_ROOT` - `MAIBOT_RUNTIME_ROOT`
- `MAIBOT_BASE_IMAGE`
No repository secrets are required for the default same-host pipeline. No repository secrets are required for the default same-host pipeline.
Bootstrap note:
- `deploy/server-maibot/Dockerfile.offline` is only for the first bootstrap or for refreshing the runtime base image.
- The normal Gitea release pipeline uses `deploy/server-maibot/Dockerfile.release`, so it does not need Docker Hub or GitHub during each deploy.
- If `pyproject.toml` or `uv.lock` changes, refresh the local base image once before relying on the release pipeline again.

View File

@@ -3,6 +3,7 @@ set -euo pipefail
release_dir="${1:?usage: activate-release.sh <release-dir>}" release_dir="${1:?usage: activate-release.sh <release-dir>}"
runtime_root="${MAIBOT_RUNTIME_ROOT:-/root/maibot-offline}" runtime_root="${MAIBOT_RUNTIME_ROOT:-/root/maibot-offline}"
base_image="${MAIBOT_BASE_IMAGE:-maibot-offline:latest}"
case "$runtime_root" in case "$runtime_root" in
/root/maibot-offline|/root/maibot-offline/*) ;; /root/maibot-offline|/root/maibot-offline/*) ;;
@@ -17,6 +18,23 @@ if [ ! -d "$release_dir" ]; then
exit 1 exit 1
fi fi
if ! docker image inspect "$base_image" >/dev/null 2>&1; then
echo "base image not found locally: $base_image" >&2
echo "bootstrap it once with deploy/server-maibot/Dockerfile.offline before using the release pipeline" >&2
exit 1
fi
for dep_file in pyproject.toml uv.lock; do
if [ -f "$runtime_root/$dep_file" ] && ! cmp -s "$release_dir/$dep_file" "$runtime_root/$dep_file"; then
echo "dependency metadata changed: $dep_file" >&2
echo "refresh the local base image with deploy/server-maibot/Dockerfile.offline before using the release pipeline" >&2
exit 1
fi
done
release_id="$(basename "$release_dir")"
release_image="maibot-offline:${release_id}"
mkdir -p \ mkdir -p \
"$runtime_root" \ "$runtime_root" \
"$runtime_root/data/MaiMBot" \ "$runtime_root/data/MaiMBot" \
@@ -28,7 +46,7 @@ mkdir -p \
"$runtime_root/docker-config/mmc" \ "$runtime_root/docker-config/mmc" \
"$runtime_root/docker-config/napcat" "$runtime_root/docker-config/napcat"
python3 - "$release_dir" "$runtime_root" <<'PY' python3 - "$release_dir/deploy/server-maibot" "$runtime_root/deploy/server-maibot" <<'PY'
from pathlib import Path from pathlib import Path
import shutil import shutil
import sys import sys
@@ -36,44 +54,23 @@ import sys
src = Path(sys.argv[1]).resolve() src = Path(sys.argv[1]).resolve()
dst = Path(sys.argv[2]).resolve() dst = Path(sys.argv[2]).resolve()
skip_names = { if dst.exists():
".git", shutil.rmtree(dst)
".gitea",
".github",
".venv",
"data",
"docker-config",
"depends-data",
"logs",
"plugins",
"acme",
"backups",
"bin",
"_staging",
}
skip_suffixes = {".pem", ".key", ".tar", ".tgz", ".zip"}
for item in src.iterdir():
if item.name in skip_names or item.suffix in skip_suffixes:
continue
target = dst / item.name
if target.exists():
if target.is_dir() and not target.is_symlink():
shutil.rmtree(target)
else:
target.unlink()
if item.is_dir():
shutil.copytree( shutil.copytree(
item, src,
target, dst,
ignore=shutil.ignore_patterns("__pycache__", "*.pyc", "*.pyo"), ignore=shutil.ignore_patterns("__pycache__", "*.pyc", "*.pyo"),
) )
else:
shutil.copy2(item, target)
PY PY
docker build \
--build-arg "MAIBOT_BASE_IMAGE=${base_image}" \
-f "$release_dir/deploy/server-maibot/Dockerfile.release" \
-t "$release_image" \
"$release_dir"
docker tag "$release_image" maibot-offline:latest
cd "$runtime_root" cd "$runtime_root"
docker build -f deploy/server-maibot/Dockerfile.offline -t maibot-offline:latest . MAIBOT_CORE_IMAGE="$release_image" docker compose -f deploy/server-maibot/docker-compose.server.yml up -d
docker compose -f deploy/server-maibot/docker-compose.server.yml up -d