重生成build文件
Lint / lint (push) Canceled after 0s

This commit is contained in:
jerron
2026-09-04 15:20:42 +08:00
parent cf06a0b8ec
commit 9ba3815e54
12 changed files with 926 additions and 41 deletions
Executable
+8
View File
@@ -0,0 +1,8 @@
#!/bin/bash
docker build \
--no-cache \
--build-arg=FRAPPE_PATH=https://git.yierp.vip/erp/frappe.git \
--build-arg=FRAPPE_BRANCH=main \
--secret=id=apps_json,src=apps.json \
--tag=yierp:15 \
--file=images/yi15/Containerfile .
Executable
+8
View File
@@ -0,0 +1,8 @@
#!/bin/bash
docker build \
--no-cache \
--build-arg=FRAPPE_PATH=https://git.yierp.vip/erp/frappe.git \
--build-arg=FRAPPE_BRANCH=main \
--secret=id=apps_json,src=apps.json \
--tag=yierp:16 \
--file=images/yi16/Containerfile .
+57 -25
View File
@@ -1,7 +1,11 @@
#!/bin/bash
set -euo pipefail
IMAGE_NAME="yierp"
IMAGE_TAG="v260831"
NO_CACHE="${NO_CACHE:-0}"
BUILD_RETRIES="${BUILD_RETRIES:-3}"
echo "🚀 开始构建 YIERP 镜像..."
echo "📦 镜像名称: ${IMAGE_NAME}:${IMAGE_TAG}"
@@ -12,30 +16,58 @@ export DOCKER_BUILDKIT=1
# 构建镜像
echo "🔨 开始构建镜像(这可能需要 5-15 分钟)..."
docker build \
--no-cache \
--build-arg=FRAPPE_PATH=https://git.yierp.vip/erp/frappe.git \
--build-arg=FRAPPE_BRANCH=main \
--build-arg=FRAPPE_VERSION=version-16 \
--secret=id=apps_json,src=apps.json \
--tag=${IMAGE_NAME}:${IMAGE_TAG} \
--file=images/yierp/Containerfile .
if [ $? -eq 0 ]; then
echo ""
echo "✅ 镜像构建成功: ${IMAGE_NAME}:${IMAGE_TAG}"
echo ""
echo "📊 镜像信息:"
docker images ${IMAGE_NAME}:${IMAGE_TAG} --format "table {{.Repository}}:{{.Tag}}\t{{.Size}}\t{{.CreatedAt}}"
# 验证镜像中的应用
echo ""
echo "🔍 验证镜像中的应用列表:"
if ! docker run --rm --user frappe --entrypoint bash ${IMAGE_NAME}:${IMAGE_TAG} -lc 'cd /home/frappe/frappe-bench && bench list-apps'; then
echo "⚠️ 应用列表校验失败,但镜像已构建完成"
fi
BUILD_CACHE_FLAG=""
if [ "$NO_CACHE" = "1" ]; then
BUILD_CACHE_FLAG="--no-cache"
echo "⚠️ 已启用无缓存构建"
else
echo "❌ 镜像构建失败,请检查错误信息"
exit 1
fi
echo "✅ 使用缓存构建(更快、更稳定)"
fi
attempt=1
while [ "$attempt" -le "$BUILD_RETRIES" ]; do
echo "🔁 构建尝试 ${attempt}/${BUILD_RETRIES}"
if docker build \
${BUILD_CACHE_FLAG} \
--build-arg=FRAPPE_PATH=https://git.yierp.vip/erp/frappe.git \
--build-arg=FRAPPE_BRANCH=main \
--build-arg=FRAPPE_VERSION=version-16 \
--secret=id=apps_json,src=apps.json \
--tag=${IMAGE_NAME}:${IMAGE_TAG} \
--file=images/yierp/Containerfile .; then
break
fi
if [ "$attempt" -eq "$BUILD_RETRIES" ]; then
echo "❌ 镜像构建失败(已重试 ${BUILD_RETRIES} 次)"
exit 1
fi
echo "⚠️ 构建失败,准备重试..."
attempt=$((attempt + 1))
done
echo ""
echo "✅ 镜像构建成功: ${IMAGE_NAME}:${IMAGE_TAG}"
echo ""
echo "📊 镜像信息:"
docker images ${IMAGE_NAME}:${IMAGE_TAG} --format "table {{.Repository}}:{{.Tag}}\t{{.Size}}\t{{.CreatedAt}}"
# 验证镜像中的应用
echo ""
echo "🔍 验证镜像中的应用列表:"
if ! docker run --rm --user frappe --entrypoint bash ${IMAGE_NAME}:${IMAGE_TAG} -lc 'cd /home/frappe/frappe-bench && bench list-apps'; then
echo "⚠️ 应用列表校验失败,但镜像已构建完成"
fi
# 验证 Node / Yarn 与资产链路(资产内容由容器启动时自动修复)
echo ""
echo "🧪 验证前端工具链与资产链路:"
docker run --rm --user frappe --entrypoint bash ${IMAGE_NAME}:${IMAGE_TAG} -lc '
set -e
cd /home/frappe/frappe-bench
node --version
yarn --version
/usr/local/bin/check-assets.sh basic || true
'
+18 -10
View File
@@ -30,10 +30,19 @@ services:
condition: service_started
command: >
bash -ec "
NODE24_BIN=$$(dirname $$(ls -1 /home/frappe/.nvm/versions/node/*/bin/node 2>/dev/null | sort | tail -n1 || true)) &&
if [ -n "$$NODE24_BIN" ]; then
ln -sf "$$NODE24_BIN/node" /usr/local/bin/node &&
ln -sf "$$NODE24_BIN/npm" /usr/local/bin/npm &&
[ -x "$$NODE24_BIN/npx" ] && ln -sf "$$NODE24_BIN/npx" /usr/local/bin/npx || true;
[ -x "$$NODE24_BIN/yarn" ] && ln -sf "$$NODE24_BIN/yarn" /usr/local/bin/yarn || true;
fi &&
mkdir -p /home/frappe/frappe-bench/sites /home/frappe/frappe-bench/logs /home/frappe/frappe-bench/config/pids &&
chown -R frappe:frappe /home/frappe/frappe-bench/sites /home/frappe/frappe-bench/logs /home/frappe/frappe-bench/config &&
cd /home/frappe/frappe-bench &&
su -s /bin/bash frappe -c 'cd /home/frappe/frappe-bench && find apps -mindepth 1 -maxdepth 1 -type d | while read -r app_dir; do [ -f "$$app_dir/modules.txt" ] && basename "$$app_dir"; done | sort > sites/apps.txt' &&
su -s /bin/bash frappe -c 'sed -i "s#air-datepicker/dist/js/datepicker.min.js#air-datepicker/air-datepicker.js#g" /home/frappe/frappe-bench/apps/frappe/frappe/public/js/controls.bundle.js && sed -i "s#~air-datepicker/dist/css/datepicker.min#air-datepicker/air-datepicker.css#g" /home/frappe/frappe-bench/apps/frappe/frappe/public/scss/common/datepicker.scss && sed -i "s#~air-datepicker/air-datepicker.css#air-datepicker/air-datepicker.css#g" /home/frappe/frappe-bench/apps/frappe/frappe/public/scss/common/datepicker.scss && sed -i "s#air-datepicker/dist/js/i18n/#air-datepicker/locale/#g" /home/frappe/frappe-bench/apps/frappe/frappe/public/js/frappe/form/controls/datepicker_i18n.js && sed -i "s#air-datepicker/locale/datepicker.#air-datepicker/locale/#g" /home/frappe/frappe-bench/apps/frappe/frappe/public/js/frappe/form/controls/datepicker_i18n.js' &&
su -s /bin/bash frappe -c 'cd /home/frappe/frappe-bench && find apps -mindepth 1 -maxdepth 1 -type d | while read -r app_dir; do app_name=$$(basename "$$app_dir"); [ -f "$$app_dir/$$app_name/modules.txt" ] && echo "$$app_name"; done | sort > sites/apps.txt' &&
su -s /bin/bash frappe -c 'node -v && yarn -v' &&
echo '=== 配置数据库连接 ===' &&
su -s /bin/bash frappe -c 'cd /home/frappe/frappe-bench && bench set-mariadb-host "'"'"'$$DB_HOST'"'"'"' &&
su -s /bin/bash frappe -c 'cd /home/frappe/frappe-bench && bench config set-common-config -c db_port "'"'"'$$DB_PORT'"'"'"' &&
@@ -47,18 +56,17 @@ services:
else
echo '=== 站点已存在,跳过创建 ===';
fi &&
mkdir -p /home/frappe/frappe-bench/sites/assets &&
if [ ! -f /home/frappe/frappe-bench/sites/assets/assets.json ]; then
echo '{}' > /home/frappe/frappe-bench/sites/assets/assets.json;
chown frappe:frappe /home/frappe/frappe-bench/sites/assets/assets.json;
echo '=== 已写入兜底 assets.json ===';
if ! /usr/local/bin/check-assets.sh full; then
echo '=== 前端资产完整检查失败,开始自动修复 ===' &&
su -s /bin/bash frappe -c 'cd /home/frappe/frappe-bench/apps/frappe && unset NODE_ENV npm_config_production && yarn install --check-files' &&
su -s /bin/bash frappe -c 'cd /home/frappe/frappe-bench && bench build --force';
else
echo '=== 前端资产完整检查通过,跳过自动修复 ===';
fi &&
echo '=== 尝试安装前端依赖并构建资源(失败不阻塞启动) ===' &&
su -s /bin/bash frappe -c 'cd /home/frappe/frappe-bench/apps/frappe && (yarn install --frozen-lockfile || yarn install)' || true &&
su -s /bin/bash frappe -c 'cd /home/frappe/frappe-bench && bench build --app frappe' || true &&
/usr/local/bin/check-assets.sh full &&
su -s /bin/bash frappe -c 'cd /home/frappe/frappe-bench && bench use frontend' &&
echo '=== 启动 ERPNext 服务 ===' &&
exec su -s /bin/bash frappe -c 'cd /home/frappe/frappe-bench && bench serve --host 0.0.0.0 --port 8000'
exec su -s /bin/bash frappe -c 'cd /home/frappe/frappe-bench && bench serve --host 0.0.0.0 --port 8000 --noreload'
"
# ============================================
+55
View File
@@ -0,0 +1,55 @@
services:
db:
image: mariadb:11.8
container_name: frappe-dev-db
restart: unless-stopped
networks:
- frappe_dev_network
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: admin
MARIADB_ROOT_PASSWORD: admin
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --skip-character-set-client-handshake
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
start_period: 10s
interval: 10s
timeout: 5s
retries: 5
volumes:
- frappe-dev-db-data:/var/lib/mysql
redis-cache:
image: redis:6.2-alpine
container_name: frappe-dev-redis-cache
restart: unless-stopped
networks:
- frappe_dev_network
ports:
- "6379:6379"
volumes:
- frappe-dev-redis-cache-data:/data
redis-queue:
image: redis:6.2-alpine
container_name: frappe-dev-redis-queue
restart: unless-stopped
networks:
- frappe_dev_network
ports:
- "6380:6379"
volumes:
- frappe-dev-redis-queue-data:/data
volumes:
frappe-dev-db-data:
frappe-dev-redis-cache-data:
frappe-dev-redis-queue-data:
networks:
frappe_dev_network:
driver: bridge
+58
View File
@@ -0,0 +1,58 @@
ARG FRAPPE_BRANCH=version-16
ARG FRAPPE_IMAGE_PREFIX=frappe
FROM frappe/build:v15 AS builder
ARG FRAPPE_BRANCH=version-16
ARG FRAPPE_PATH=https://git.yierp.vip/erp/frappe.git
ARG CACHE_BUST=""
USER frappe
RUN --mount=type=secret,id=apps_json,target=/opt/frappe/apps.json,uid=1000,gid=1000 \
: "${CACHE_BUST}" && \
export APP_INSTALL_ARGS="" && \
if [ -f /opt/frappe/apps.json ] && [ -s /opt/frappe/apps.json ]; then \
export APP_INSTALL_ARGS="--apps_path=/opt/frappe/apps.json"; \
fi && \
bench init ${APP_INSTALL_ARGS}\
--frappe-branch=${FRAPPE_BRANCH} \
--frappe-path=${FRAPPE_PATH} \
--no-procfile \
--no-backups \
--skip-redis-config-generation \
--verbose \
/home/frappe/frappe-bench && \
cd /home/frappe/frappe-bench && \
echo "{}" > sites/common_site_config.json && \
find apps -mindepth 1 -path "*/.git" | xargs rm -fr
FROM frappe/base:v15 AS backend
USER frappe
COPY --from=builder --chown=frappe:frappe /home/frappe/frappe-bench /home/frappe/frappe-bench
WORKDIR /home/frappe/frappe-bench
# Move assets to image-layer storage
RUN cp -r /home/frappe/frappe-bench/sites/assets /home/frappe/frappe-bench/assets && \
rm -rf /home/frappe/frappe-bench/sites/assets
VOLUME [ \
"/home/frappe/frappe-bench/sites", \
"/home/frappe/frappe-bench/logs" \
]
USER root
# This entrypoint script link build assets of the image to the mounted sites volume at container initialization
COPY resources/core/main-entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod 755 /usr/local/bin/entrypoint.sh
COPY resources/core/start.sh /usr/local/bin/start.sh
RUN chmod 755 /usr/local/bin/start.sh
USER frappe
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
CMD ["start.sh"]
+58
View File
@@ -0,0 +1,58 @@
ARG FRAPPE_BRANCH=version-16
ARG FRAPPE_IMAGE_PREFIX=frappe
FROM frappe/build:v16 AS builder
ARG FRAPPE_BRANCH=version-16
ARG FRAPPE_PATH=https://git.yierp.vip/erp/frappe.git
ARG CACHE_BUST=""
USER frappe
RUN --mount=type=secret,id=apps_json,target=/opt/frappe/apps.json,uid=1000,gid=1000 \
: "${CACHE_BUST}" && \
export APP_INSTALL_ARGS="" && \
if [ -f /opt/frappe/apps.json ] && [ -s /opt/frappe/apps.json ]; then \
export APP_INSTALL_ARGS="--apps_path=/opt/frappe/apps.json"; \
fi && \
bench init ${APP_INSTALL_ARGS}\
--frappe-branch=${FRAPPE_BRANCH} \
--frappe-path=${FRAPPE_PATH} \
--no-procfile \
--no-backups \
--skip-redis-config-generation \
--verbose \
/home/frappe/frappe-bench && \
cd /home/frappe/frappe-bench && \
echo "{}" > sites/common_site_config.json && \
find apps -mindepth 1 -path "*/.git" | xargs rm -fr
FROM frappe/base:v16 AS backend
USER frappe
COPY --from=builder --chown=frappe:frappe /home/frappe/frappe-bench /home/frappe/frappe-bench
WORKDIR /home/frappe/frappe-bench
# Move assets to image-layer storage
RUN cp -r /home/frappe/frappe-bench/sites/assets /home/frappe/frappe-bench/assets && \
rm -rf /home/frappe/frappe-bench/sites/assets
VOLUME [ \
"/home/frappe/frappe-bench/sites", \
"/home/frappe/frappe-bench/logs" \
]
USER root
# This entrypoint script link build assets of the image to the mounted sites volume at container initialization
COPY resources/core/main-entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod 755 /usr/local/bin/entrypoint.sh
COPY resources/core/start.sh /usr/local/bin/start.sh
RUN chmod 755 /usr/local/bin/start.sh
USER frappe
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
CMD ["start.sh"]
+84 -4
View File
@@ -8,8 +8,8 @@ FROM ${FRAPPE_IMAGE_PREFIX}/build:${FRAPPE_VERSION} AS builder
USER root
RUN mkdir -p /home/frappe && chown frappe:frappe /home/frappe && \
apt-get update && \
apt-get install -y --no-install-recommends nodejs npm && \
npm install -g yarn && \
apt-get install -y --no-install-recommends nodejs npm yarnpkg && \
ln -sf /usr/bin/yarnpkg /usr/local/bin/yarn && \
rm -rf /var/lib/apt/lists/*
ARG FRAPPE_BRANCH
@@ -20,9 +20,25 @@ WORKDIR /home/frappe
WORKDIR /home/frappe/frappe-bench
# 降低 git/yarn 在弱网络下的握手失败概率
RUN git config --global http.version HTTP/1.1 && \
git config --global http.lowSpeedLimit 0 && \
git config --global http.lowSpeedTime 999999 && \
yarn config set network-timeout 600000 -g
# 克隆 Frappe 到 bench apps 目录(bench 运行期依赖该布局)
RUN mkdir -p apps && git clone --depth 1 -b ${FRAPPE_BRANCH} ${FRAPPE_PATH} apps/frappe
# 将 air-datepicker 从 GitHub git 依赖替换为 npm 包,避免外网 TLS 抖动
RUN node -e 'const fs=require("fs"); const p="/home/frappe/frappe-bench/apps/frappe/package.json"; const pkg=JSON.parse(fs.readFileSync(p,"utf8")); if(pkg.dependencies && pkg.dependencies["air-datepicker"]){ pkg.dependencies["air-datepicker"]="3.6.0"; } fs.writeFileSync(p, JSON.stringify(pkg, null, 2)+"\n");'
# 将 GitHub git 依赖替换为 codeload tarball,避免 git clone TLS 失败
RUN set -e; \
for f in /home/frappe/frappe-bench/apps/frappe/pyproject.toml /home/frappe/frappe-bench/apps/frappe/requirements*.txt; do \
[ -f "$f" ] || continue; \
sed -i -E 's#git\+https://github.com/([^/]+/[^@"]+)@([0-9A-Za-z._-]+)#https://codeload.github.com/\1/tar.gz/\2#g' "$f"; \
done
# 创建 Python 虚拟环境
RUN python3 -m venv env
@@ -34,14 +50,43 @@ RUN python3 -m pip install --no-cache-dir --upgrade pip wheel
# 复制项目文件
COPY --chown=frappe:frappe . .
# 新版 air-datepicker 不再提供 dist 路径,改写 Frappe 里的旧引用并校验
RUN sed -i 's#air-datepicker/dist/js/datepicker.min.js#air-datepicker/air-datepicker.js#g' /home/frappe/frappe-bench/apps/frappe/frappe/public/js/controls.bundle.js && \
sed -i 's#~air-datepicker/dist/css/datepicker.min#air-datepicker/air-datepicker.css#g' /home/frappe/frappe-bench/apps/frappe/frappe/public/scss/common/datepicker.scss && \
sed -i 's#~air-datepicker/air-datepicker.css#air-datepicker/air-datepicker.css#g' /home/frappe/frappe-bench/apps/frappe/frappe/public/scss/common/datepicker.scss && \
sed -i -E 's#air-datepicker/dist/js/i18n/datepicker\.([A-Za-z-]+)\.js#air-datepicker/locale/\1.js#g' /home/frappe/frappe-bench/apps/frappe/frappe/public/js/frappe/form/controls/datepicker_i18n.js && \
sed -i -E 's#air-datepicker/locale/datepicker\.([A-Za-z-]+)\.js#air-datepicker/locale/\1.js#g' /home/frappe/frappe-bench/apps/frappe/frappe/public/js/frappe/form/controls/datepicker_i18n.js && \
! grep -q 'air-datepicker/dist/' /home/frappe/frappe-bench/apps/frappe/frappe/public/js/controls.bundle.js && \
! grep -q 'air-datepicker/dist/' /home/frappe/frappe-bench/apps/frappe/frappe/public/scss/common/datepicker.scss && \
! grep -q 'air-datepicker/dist/' /home/frappe/frappe-bench/apps/frappe/frappe/public/js/frappe/form/controls/datepicker_i18n.js
# 验证 Node 安装
RUN node --version && npm --version
# 强制安装 frappe 前端依赖,避免运行时缺少 fast-glob 等模块
RUN cd /home/frappe/frappe-bench/apps/frappe && \
unset NODE_ENV npm_config_production && \
for i in 1 2 3; do \
yarn install --check-files && break; \
if [ "$i" -eq 3 ]; then \
echo "❌ yarn install failed after 3 attempts"; \
exit 1; \
fi; \
echo "⚠️ yarn install failed, retrying (attempt $((i + 1))/3)"; \
done
# 安装 frappe
RUN python3 -m pip install --no-cache-dir -e /home/frappe/frappe-bench/apps/frappe
RUN for i in 1 2 3; do \
python3 -m pip install --no-cache-dir -e /home/frappe/frappe-bench/apps/frappe && break; \
if [ "$i" -eq 3 ]; then \
echo "❌ pip install frappe failed after 3 attempts"; \
exit 1; \
fi; \
echo "⚠️ pip install frappe failed, retrying (attempt $((i + 1))/3)"; \
done
# 安装 apps.json 中定义的应用
RUN --mount=type=secret,id=apps_json \
RUN --mount=type=secret,id=apps_json,uid=1000,gid=1000,mode=0444 \
if [ -f /run/secrets/apps_json ]; then \
echo "📦 从 apps.json 安装应用..."; \
export APPS_JSON_BASE64=$(cat /run/secrets/apps_json | base64 -w 0); \
@@ -73,15 +118,50 @@ RUN --mount=type=secret,id=apps_json \
# 清理 .git 目录以减小镜像大小
RUN find apps -mindepth 1 -path "*/.git" | xargs rm -fr
# 为所有包含 package.json 的应用安装前端依赖
RUN set -e; \
cd /home/frappe/frappe-bench; \
find apps -mindepth 1 -maxdepth 1 -type d | while read -r app_dir; do \
if [ -f "$app_dir/package.json" ]; then \
echo "Installing JS deps for $(basename "$app_dir")"; \
(cd "$app_dir" && unset NODE_ENV npm_config_production && \
for i in 1 2 3; do \
yarn install --check-files && break; \
if [ "$i" -eq 3 ]; then \
echo "❌ yarn install failed for $(basename "$app_dir") after 3 attempts"; \
exit 1; \
fi; \
echo "⚠️ yarn install failed for $(basename "$app_dir"), retrying (attempt $((i + 1))/3)"; \
done); \
fi; \
done
FROM ${FRAPPE_IMAGE_PREFIX}/base:${FRAPPE_VERSION} AS backend
USER root
RUN apt-get update && \
apt-get install -y --no-install-recommends nodejs npm yarnpkg && \
ln -sf /usr/bin/yarnpkg /usr/local/bin/yarn && \
rm -rf /var/lib/apt/lists/*
# 优先使用 base 镜像中 nvm 的 Node 24,满足前端构建对 node>=24 的要求
RUN set -e; \
NODE_BIN_DIR="$(dirname "$(ls -1 /home/frappe/.nvm/versions/node/*/bin/node | sort | tail -n1)")"; \
ln -sf "$NODE_BIN_DIR/node" /usr/local/bin/node; \
ln -sf "$NODE_BIN_DIR/npm" /usr/local/bin/npm; \
ln -sf "$NODE_BIN_DIR/npx" /usr/local/bin/npx; \
if [ -x "$NODE_BIN_DIR/yarn" ]; then ln -sf "$NODE_BIN_DIR/yarn" /usr/local/bin/yarn; fi
# 复制完整 bench(含 env),确保运行时具备 frappe 依赖
COPY --from=builder /home/frappe/frappe-bench /home/frappe/frappe-bench
COPY resources/core/main-entrypoint.sh /usr/local/bin/main-entrypoint.sh
RUN chmod 755 /usr/local/bin/main-entrypoint.sh
COPY resources/core/check-assets.sh /usr/local/bin/check-assets.sh
RUN chmod 755 /usr/local/bin/check-assets.sh
WORKDIR /home/frappe/frappe-bench
ENV PATH="/home/frappe/frappe-bench/env/bin:${PATH}"
+86
View File
@@ -0,0 +1,86 @@
#!/bin/bash
set -euo pipefail
BENCH_PATH="/home/frappe/frappe-bench"
SITES_ASSETS_LINK="${BENCH_PATH}/sites/assets"
ASSETS_DIR="${BENCH_PATH}/assets"
ASSETS_JSON="${ASSETS_DIR}/assets.json"
MODE="${1:-full}"
echo "=== 前端资产自检开始 ==="
if [ ! -L "$SITES_ASSETS_LINK" ]; then
echo "❌ 失败: ${SITES_ASSETS_LINK} 不是软链接"
exit 1
fi
LINK_TARGET="$(readlink "$SITES_ASSETS_LINK")"
if [ "$LINK_TARGET" != "$ASSETS_DIR" ]; then
echo "❌ 失败: sites/assets 软链接目标错误: ${LINK_TARGET}"
exit 1
fi
if [ ! -f "$ASSETS_JSON" ]; then
echo "❌ 失败: 未找到 ${ASSETS_JSON}"
exit 1
fi
if [ "$MODE" = "basic" ]; then
echo "✅ 基础检查通过: 软链接与 assets.json 存在"
exit 0
fi
python3 - <<'PY'
import json
import os
import sys
bench = "/home/frappe/frappe-bench"
assets_dir = os.path.join(bench, "assets")
assets_json = os.path.join(assets_dir, "assets.json")
with open(assets_json, "r", encoding="utf-8") as f:
data = json.load(f)
css_paths = []
def walk(value):
if isinstance(value, dict):
for v in value.values():
walk(v)
elif isinstance(value, list):
for item in value:
walk(item)
elif isinstance(value, str) and value.endswith(".css"):
css_paths.append(value)
walk(data)
if not css_paths:
print("❌ 失败: assets.json 中未发现 CSS 资源路径")
sys.exit(1)
missing = []
for p in css_paths:
rel = p
if rel.startswith("/assets/"):
rel = rel[len("/assets/"):]
elif rel.startswith("assets/"):
rel = rel[len("assets/"):]
rel = rel.lstrip("/")
full = os.path.join(assets_dir, rel)
if not os.path.isfile(full):
missing.append((p, full))
if missing:
print("❌ 失败: 下列 CSS 资源在磁盘上不存在:")
for src, full in missing[:10]:
print(f" - {src} -> {full}")
if len(missing) > 10:
print(f" ... 还有 {len(missing) - 10} 个")
sys.exit(1)
print(f"✅ 通过: 共检测到 {len(css_paths)} 个 CSS 路径,文件均存在")
PY
echo "=== 前端资产自检通过 ==="
+3 -2
View File
@@ -17,8 +17,9 @@ mkdir -p "$SITES_PATH" "$LOGS_PATH" "$BAKED_PATH" "$CONFIG_PATH" "$PIDS_PATH" "$
# Rebuild apps.txt from app directories that look like Frappe apps.
find "$APPS_PATH" -mindepth 1 -maxdepth 1 -type d | while read -r app_dir; do
if [ -f "$app_dir/modules.txt" ]; then
basename "$app_dir"
app_name="$(basename "$app_dir")"
if [ -f "$app_dir/$app_name/modules.txt" ]; then
echo "$app_name"
fi
done | sort > "$APPS_TXT_PATH"
+244
View File
@@ -0,0 +1,244 @@
services:
backend:
image: yierp:15
networks:
- frappe_network
deploy:
restart_policy:
condition: on-failure
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
environment:
DB_HOST: db
DB_PORT: "3306"
MYSQL_ROOT_PASSWORD: admin
MARIADB_ROOT_PASSWORD: admin
depends_on:
configurator:
condition: service_completed_successfully
configurator:
image: yierp:15
networks:
- frappe_network
deploy:
restart_policy:
condition: none
entrypoint:
- bash
- -c
command:
- >
ls -1 apps > sites/apps.txt;
bench set-config -g db_host $$DB_HOST;
bench set-config -gp db_port $$DB_PORT;
bench set-config -g redis_cache "redis://$$REDIS_CACHE";
bench set-config -g redis_queue "redis://$$REDIS_QUEUE";
bench set-config -g redis_socketio "redis://$$REDIS_QUEUE";
bench set-config -gp socketio_port $$SOCKETIO_PORT;
environment:
DB_HOST: db
DB_PORT: "3306"
REDIS_CACHE: redis-cache:6379
REDIS_QUEUE: redis-queue:6379
SOCKETIO_PORT: "9000"
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
create-site:
image: yierp:15
networks:
- frappe_network
deploy:
restart_policy:
condition: none
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
entrypoint:
- bash
- -c
command:
- >
wait-for-it -t 120 db:3306;
wait-for-it -t 120 redis-cache:6379;
wait-for-it -t 120 redis-queue:6379;
export start=`date +%s`;
until [[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".db_host // empty"` ]] && \
[[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".redis_cache // empty"` ]] && \
[[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".redis_queue // empty"` ]];
do
echo "Waiting for sites/common_site_config.json to be created";
sleep 5;
if (( `date +%s`-start > 120 )); then
echo "could not find sites/common_site_config.json with required keys";
exit 1
fi
done;
echo "sites/common_site_config.json found";
if [ -d "sites/frontend" ]; then
echo "Site frontend already exists, skipping creation";
else
bench new-site --mariadb-user-host-login-scope='%' --admin-password=admin --db-root-username=root --db-root-password=admin --install-app erpnext --set-default frontend;
fi;
db:
image: mariadb:11.8
networks:
- frappe_network
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
start_period: 5s
interval: 5s
timeout: 5s
retries: 5
deploy:
restart_policy:
condition: on-failure
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --skip-character-set-client-handshake
environment:
MYSQL_ROOT_PASSWORD: admin
MARIADB_ROOT_PASSWORD: admin
volumes:
- db-data:/var/lib/mysql
frontend:
image: yierp:15
networks:
- frappe_network
depends_on:
- websocket
- backend
deploy:
restart_policy:
condition: on-failure
command:
- nginx-entrypoint.sh
environment:
BACKEND: backend:8000
FRAPPE_SITE_NAME_HEADER: frontend
SOCKETIO: websocket:9000
UPSTREAM_REAL_IP_ADDRESS: 127.0.0.1
UPSTREAM_REAL_IP_HEADER: X-Forwarded-For
UPSTREAM_REAL_IP_RECURSIVE: "off"
PROXY_READ_TIMEOUT: 120
CLIENT_MAX_BODY_SIZE: 50m
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
ports:
- "8080:8080"
queue-long:
image: yierp:15
networks:
- frappe_network
deploy:
restart_policy:
condition: on-failure
command:
- bench
- worker
- --queue
- long,default,short
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
environment:
FRAPPE_REDIS_CACHE: redis://redis-cache:6379
FRAPPE_REDIS_QUEUE: redis://redis-queue:6379
depends_on:
configurator:
condition: service_completed_successfully
queue-short:
image: yierp:15
networks:
- frappe_network
deploy:
restart_policy:
condition: on-failure
command:
- bench
- worker
- --queue
- short,default
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
environment:
FRAPPE_REDIS_CACHE: redis://redis-cache:6379
FRAPPE_REDIS_QUEUE: redis://redis-queue:6379
depends_on:
configurator:
condition: service_completed_successfully
redis-queue:
image: redis:6.2-alpine
networks:
- frappe_network
deploy:
restart_policy:
condition: on-failure
volumes:
- redis-queue-data:/data
redis-cache:
image: redis:6.2-alpine
networks:
- frappe_network
deploy:
restart_policy:
condition: on-failure
scheduler:
image: yierp:15
networks:
- frappe_network
deploy:
restart_policy:
condition: on-failure
command:
- bench
- schedule
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
depends_on:
configurator:
condition: service_completed_successfully
websocket:
image: yierp:15
networks:
- frappe_network
deploy:
restart_policy:
condition: on-failure
command:
- node
- /home/frappe/frappe-bench/apps/frappe/socketio.js
environment:
FRAPPE_REDIS_CACHE: redis://redis-cache:6379
FRAPPE_REDIS_QUEUE: redis://redis-queue:6379
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
depends_on:
configurator:
condition: service_completed_successfully
volumes:
db-data:
redis-queue-data:
sites:
logs:
networks:
frappe_network:
driver: bridge
+247
View File
@@ -0,0 +1,247 @@
services:
backend:
image: yierp:16
networks:
- frappe_network
deploy:
restart_policy:
condition: on-failure
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
environment:
DB_HOST: db
DB_PORT: "3306"
MYSQL_ROOT_PASSWORD: admin
MARIADB_ROOT_PASSWORD: admin
depends_on:
configurator:
condition: service_completed_successfully
configurator:
image: yierp:16
networks:
- frappe_network
deploy:
restart_policy:
condition: none
entrypoint:
- bash
- -c
command:
- >
ls -1 apps > sites/apps.txt;
bench set-config -g db_host $$DB_HOST;
bench set-config -gp db_port $$DB_PORT;
bench set-config -g redis_cache "redis://$$REDIS_CACHE";
bench set-config -g redis_queue "redis://$$REDIS_QUEUE";
bench set-config -g redis_socketio "redis://$$REDIS_QUEUE";
bench set-config -gp socketio_port $$SOCKETIO_PORT;
environment:
DB_HOST: db
DB_PORT: "3306"
REDIS_CACHE: redis-cache:6379
REDIS_QUEUE: redis-queue:6379
SOCKETIO_PORT: "9000"
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
create-site:
image: yierp:16
networks:
- frappe_network
deploy:
restart_policy:
condition: none
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
entrypoint:
- bash
- -c
command:
- >
wait-for-it -t 120 db:3306;
wait-for-it -t 120 redis-cache:6379;
wait-for-it -t 120 redis-queue:6379;
export start=`date +%s`;
until [[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".db_host // empty"` ]] && \
[[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".redis_cache // empty"` ]] && \
[[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".redis_queue // empty"` ]];
do
echo "Waiting for sites/common_site_config.json to be created";
sleep 5;
if (( `date +%s`-start > 120 )); then
echo "could not find sites/common_site_config.json with required keys";
exit 1
fi
done;
echo "sites/common_site_config.json found";
if [ -d "sites/frontend" ]; then
echo "Site frontend already exists, skipping creation";
else
bench new-site --mariadb-user-host-login-scope='%' --admin-password=admin --db-root-username=root --db-root-password=admin --install-app erpnext --set-default frontend;
fi;
db:
image: mariadb:11.8
networks:
- frappe_network
ports:
- "3306:3306"
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
start_period: 5s
interval: 5s
timeout: 5s
retries: 5
deploy:
restart_policy:
condition: on-failure
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --skip-character-set-client-handshake
environment:
MYSQL_ROOT_PASSWORD: admin
MARIADB_ROOT_PASSWORD: admin
volumes:
- db-data:/var/lib/mysql
frontend:
image: yierp:16
networks:
- frappe_network
depends_on:
- websocket
- backend
deploy:
restart_policy:
condition: on-failure
command:
- nginx-entrypoint.sh
environment:
BACKEND: backend:8000
FRAPPE_SITE_NAME_HEADER: frontend
SOCKETIO: websocket:9000
UPSTREAM_REAL_IP_ADDRESS: 127.0.0.1
UPSTREAM_REAL_IP_HEADER: X-Forwarded-For
UPSTREAM_REAL_IP_RECURSIVE: "off"
PROXY_READ_TIMEOUT: 120
CLIENT_MAX_BODY_SIZE: 50m
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
ports:
- "8080:8080"
queue-long:
image: yierp:16
networks:
- frappe_network
deploy:
restart_policy:
condition: on-failure
command:
- bench
- worker
- --queue
- long,default,short
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
environment:
FRAPPE_REDIS_CACHE: redis://redis-cache:6379
FRAPPE_REDIS_QUEUE: redis://redis-queue:6379
depends_on:
configurator:
condition: service_completed_successfully
queue-short:
image: yierp:16
networks:
- frappe_network
deploy:
restart_policy:
condition: on-failure
command:
- bench
- worker
- --queue
- short,default
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
environment:
FRAPPE_REDIS_CACHE: redis://redis-cache:6379
FRAPPE_REDIS_QUEUE: redis://redis-queue:6379
depends_on:
configurator:
condition: service_completed_successfully
redis-queue:
image: redis:6.2-alpine
networks:
- frappe_network
deploy:
restart_policy:
condition: on-failure
volumes:
- redis-queue-data:/data
redis-cache:
image: redis:6.2-alpine
networks:
- frappe_network
deploy:
restart_policy:
condition: on-failure
scheduler:
image: yierp:16
networks:
- frappe_network
deploy:
restart_policy:
condition: on-failure
command:
- bench
- schedule
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
depends_on:
configurator:
condition: service_completed_successfully
websocket:
image: yierp:16
networks:
- frappe_network
deploy:
restart_policy:
condition: on-failure
command:
- node
- /home/frappe/frappe-bench/apps/frappe/socketio.js
environment:
FRAPPE_REDIS_CACHE: redis://redis-cache:6379
FRAPPE_REDIS_QUEUE: redis://redis-queue:6379
volumes:
- sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs
depends_on:
configurator:
condition: service_completed_successfully
volumes:
db-data:
redis-queue-data:
sites:
logs:
networks:
frappe_network:
driver: bridge