Files
xiaoxia-saas/scripts/build_release_images.sh
CI Bot 819d3ae23d
CI/CD Pipeline / Validate Code Quality And Tests (pull_request) Successful in 37s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 1m10s
CI/CD Pipeline / Build & Push Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Build Production Runtime Images (pull_request) Has been skipped
CI/CD Pipeline / Unit Tests (pull_request) Successful in 1m25s
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / Integration Tests (pull_request) Successful in 2m1s
fix: prevent docker images grep from failing build with set -e
The last line of the build script uses grep to display built images,
but with set -e, grep returning no matches causes the entire script
to fail. This is a classic set -e footgun.

The grep pattern was also broken: docker images shows REPOSITORY and
TAG as separate columns (no ':' between them), so 'xiaoxia-saas.*:'
never matched. Changed to two-stage grep and added || true safety net.

The build and push were actually succeeding - only the final display
line was causing the failure.
2026-07-14 09:39:31 +08:00

183 lines
5.9 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/sh
set -eu
VERSION="${1:-${RELEASE_VERSION:-}}"
if [ -z "$VERSION" ]; then
echo "Usage: $0 <version> [staging|production]"
echo "Example: $0 v0.1.5 production"
echo " $0 abc1234 staging"
exit 1
fi
# 环境参数:staging 或 production(默认 production
BUILD_ENV="${2:-production}"
case "$BUILD_ENV" in
staging) NGINX_CONF_FILE="infra/docker/nginx-staging.conf" ;;
*) NGINX_CONF_FILE="infra/docker/nginx-production.conf" ;;
esac
echo "Build environment: $BUILD_ENV → nginx config: $NGINX_CONF_FILE"
ROOT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)"
cd "$ROOT_DIR"
if docker ps --format "{{.Names}}" | grep -Eq "^(xiaoxia-(api|web|worker|postgres|redis)-production|gitea)$"; then
if [ "${ALLOW_SHARED_PRODUCTION_BUILD_HOST:-false}" != "true" ]; then
echo "Refusing to build runtime images on a host that is running production services."
exit 1
fi
fi
# ---- Registry 配置 ----
REGISTRY="${REGISTRY:-git.xiaoxiajianji.com/xiaoxia/xiaoxia-saas}"
CACHE_REGISTRY="${CACHE_REGISTRY:-git.xiaoxiajianji.com/xiaoxia/xiaoxia-saas}"
# 主缓存 tag:develop 分支构建时写入,所有分支读取
CACHE_TAG_PRIMARY="${CACHE_TAG:-develop}"
API_IMAGE="xiaoxia-saas-api:$VERSION"
WORKER_IMAGE="xiaoxia-saas-worker:$VERSION"
WEB_IMAGE="xiaoxia-saas-web:$VERSION"
API_LATEST="xiaoxia-saas-api:dev"
WORKER_LATEST="xiaoxia-saas-worker:dev"
# Registry 上的完整镜像名
REGISTRY_API="${REGISTRY}/xiaoxia-saas-api:$VERSION"
REGISTRY_WORKER="${REGISTRY}/xiaoxia-saas-worker:$VERSION"
REGISTRY_WEB="${REGISTRY}/xiaoxia-saas-web:$VERSION"
USE_CACHE=0
USE_PUSH=0
CACHE_WRITE=0
# 检查 buildx 和 Registry 认证
if docker buildx version >/dev/null 2>&1; then
if [ -n "${REGISTRY_TOKEN:-}" ]; then
printf "%s" "${REGISTRY_TOKEN}" | docker login git.xiaoxiajianji.com -u xiaoxia --password-stdin 2>/dev/null && USE_CACHE=1 && USE_PUSH=1
fi
docker buildx use default 2>/dev/null || true
fi
# ---- 缓存读写策略(按分支隔离)----
# 默认只读不写,防止 feature 分支污染主缓存
# 只有 develop/main 分支才写回缓存
BRANCH_NAME="${GITHUB_REF_NAME:-${CI_COMMIT_BRANCH:-unknown}}"
if [ "$USE_CACHE" -eq 1 ]; then
docker buildx build \
--build-arg APP_VERSION="$VERSION" \
--cache-from "type=registry,ref=${CACHE_REGISTRY}/api-cache:${CACHE_TAG_PRIMARY},ignore-error=true" \
-f infra/docker/api.Dockerfile \
-t "$API_IMAGE" -t "$API_LATEST" \
--load \
.
else
docker build --pull=false --build-arg APP_VERSION="$VERSION" -f infra/docker/api.Dockerfile -t "$API_IMAGE" -t "$API_LATEST" .
fi
build_with_cache() {
# usage: build_with_cache <image_name> <dockerfile> <extra_args...>
IMG_NAME="$1"
DOCKERFILE="$2"
shift 2
EXTRA_ARGS="$*"
CACHE_FROM="type=registry,ref=${CACHE_REGISTRY}/${IMG_NAME}-cache:${CACHE_TAG_PRIMARY},ignore-error=true"
if [ "$CACHE_WRITE" -eq 1 ]; then
CACHE_TO="type=registry,ref=${CACHE_REGISTRY}/${IMG_NAME}-cache:${CACHE_TAG_PRIMARY},mode=max"
echo " cache: read+write from ${CACHE_REGISTRY}/${IMG_NAME}-cache:${CACHE_TAG_PRIMARY}"
else
CACHE_TO=""
echo " cache: read-only from ${CACHE_REGISTRY}/${IMG_NAME}-cache:${CACHE_TAG_PRIMARY}"
fi
if [ "$USE_CACHE" -eq 1 ]; then
if [ -n "$CACHE_TO" ]; then
docker buildx build \
$EXTRA_ARGS \
--cache-from "$CACHE_FROM" \
--cache-to "$CACHE_TO" \
-f "$DOCKERFILE" \
-t "$IMG_NAME:$VERSION" \
--load \
.
else
docker buildx build \
$EXTRA_ARGS \
--cache-from "$CACHE_FROM" \
-f "$DOCKERFILE" \
-t "$IMG_NAME:$VERSION" \
--load \
.
fi
else
docker build --pull=false $EXTRA_ARGS -f "$DOCKERFILE" -t "$IMG_NAME:$VERSION" .
fi
}
echo "=== Building API image ==="
build_with_cache "api" "infra/docker/api.Dockerfile" \
"--build-arg APP_VERSION=$VERSION"
docker tag "$API_IMAGE" "$API_LATEST"
echo "=== Building Worker image ==="
if [ "$USE_CACHE" -eq 1 ]; then
docker buildx build \
--build-arg APP_VERSION="$VERSION" \
--cache-from "type=registry,ref=${CACHE_REGISTRY}/worker-cache:${CACHE_TAG_PRIMARY},ignore-error=true" \
-f infra/docker/worker.Dockerfile \
-t "$WORKER_IMAGE" -t "$WORKER_LATEST" \
--load \
.
else
docker build --pull=false --build-arg APP_VERSION="$VERSION" -f infra/docker/worker.Dockerfile -t "$WORKER_IMAGE" -t "$WORKER_LATEST" .
fi
echo "=== Building Web image (with buildx cache) ==="
# 先构建前端产物(使用持久化 npm 缓存卷)
NPM_CACHE_VOLUME="xiaoxia-npm-cache"
if ! docker volume inspect "$NPM_CACHE_VOLUME" >/dev/null 2>&1; then
docker volume create "$NPM_CACHE_VOLUME" >/dev/null
echo " Created npm cache volume: $NPM_CACHE_VOLUME"
fi
docker run --rm \
-v "$PWD:/workspace" \
-v "$NPM_CACHE_VOLUME:/workspace/apps/web/node_modules" \
-w /workspace/apps/web \
docker.m.daocloud.io/library/node:20 \
sh -lc "npm ci && npm run build"
test -f apps/web/dist/index.html
if [ "$USE_CACHE" -eq 1 ]; then
docker buildx build \
--cache-from "type=registry,ref=${CACHE_REGISTRY}/web-cache:${CACHE_TAG_PRIMARY},ignore-error=true" \
-f infra/docker/web-artifact.Dockerfile \
--build-arg "NGINX_CONF=$NGINX_CONF_FILE" \
-t "$WEB_IMAGE" \
--load \
.
else
docker build --pull=false \
-f infra/docker/web-artifact.Dockerfile \
--build-arg "NGINX_CONF=$NGINX_CONF_FILE" \
-t "$WEB_IMAGE" \
.
fi
# Push 到 Registry
if [ "$USE_PUSH" -eq 1 ]; then
echo "=== Pushing images to Registry ==="
docker tag "$API_IMAGE" "$REGISTRY_API"
docker tag "$WORKER_IMAGE" "$REGISTRY_WORKER"
docker tag "$WEB_IMAGE" "$REGISTRY_WEB"
docker push "$REGISTRY_API"
docker push "$REGISTRY_WORKER"
docker push "$REGISTRY_WEB"
echo "All images pushed to $REGISTRY"
else
echo "Registry push skipped (no auth token available)"
fi
echo "=== Build complete ==="
docker images | grep "xiaoxia-saas" | grep "$VERSION" || true