--- kind: pipeline type: docker name: par-build volumes: - name: maven-cache host: path: /tmp/drone-maven-cache - name: npm-cache host: path: /tmp/drone-npm-cache - name: dockersock host: path: /var/run/docker.sock trigger: event: [push] branch: [dev, test, main] exclude: commit: - ^\[skip ci\] - ^\[ci skip\] platform: os: linux arch: amd64 workspace: path: /drone/src environment: # 仓库配置 LOCAL_HARBOR_REG: "192.168.31.253" LOCAL_HARBOR_PROJ: "yescent" # 镜像/容器通用 IMAGE_NAME: "${DRONE_REPO_NAME}" CONTAINER_NAME: "par-server" # 部署配置 SERVER_PORT: "8080" APP_PORT: "9012" TZ: "Asia/Shanghai" steps: # ===================== 调试信息 ===================== - name: print-drone-vars image: 192.168.31.253/library/alpine:latest commands: - echo "===== ALL DRONE ENV VARIABLES =====" - env | grep -i drone | sort # ===================== 准备 Maven 缓存 ===================== - name: prepare-cache image: 192.168.31.253/library/maven-java21:3.9.9-alpine volumes: - name: maven-cache path: /host-cache commands: - | echo "正在准备Maven缓存目录..." mkdir -p /host-cache/repository if [ "$(ls -A /host-cache/repository 2>/dev/null)" ]; then CACHE_SIZE=$(du -sh /host-cache/repository | cut -f1) echo "检测到现有缓存,大小: $CACHE_SIZE" echo "CACHE_EXISTS=true" > /drone/src/.cache_info else echo "未发现现有缓存,将初始化新缓存。" echo "CACHE_EXISTS=false" > /drone/src/.cache_info fi # ===================== 前端构建(npm) ===================== - name: frontend-build image: 192.168.31.253/library/node:22-alpine volumes: - name: npm-cache path: /root/.npm environment: NPM_CONFIG_REGISTRY: "https://registry.npmmirror.com" commands: - | echo "====== 开始前端构建 ======" npm config set registry https://registry.npmmirror.com cd web npm ci --prefer-offline --ignore-scripts 2>/dev/null || npm install --prefer-offline --ignore-scripts # 确保 rollup 原生绑定可用 if [ -f node_modules/@rollup/rollup-linux-x64-musl/package.json ]; then node -e "require('@rollup/rollup-linux-x64-musl/package.json')" 2>/dev/null || npm install @rollup/rollup-linux-x64-musl --ignore-scripts --force fi npm run build echo "前端构建完成,产物列表:" ls -la dist/ echo "====== 前端构建结束 ======" # ===================== Maven 构建(含前端静态资源) ===================== - name: maven-build image: 192.168.31.253/library/maven-java21:3.9.9-alpine pull: if-not-exists environment: MAVEN_OPTS: "-Dmaven.repo.local=/root/.m2/repository -Xms512m -Xmx1024m -Djava.awt.headless=true" volumes: - name: maven-cache path: /root/.m2 commands: - | set -e echo "====== 开始 Maven 构建 ======" # 配置阿里云 Maven 镜像加速 mkdir -p /root/.m2 cat > /root/.m2/settings.xml << 'EOF' aliyunmaven 阿里云公共仓库 https://maven.aliyun.com/repository/public central EOF echo "已配置阿里云 Maven 镜像" # 将前端构建产物复制到 static 目录 FRONTEND_DIST="/drone/src/web/dist" STATIC_DIR="/drone/src/par-api/src/main/resources/static" if [ -d "$FRONTEND_DIST" ]; then mkdir -p "$STATIC_DIR" cp -r "$FRONTEND_DIST"/* "$STATIC_DIR"/ echo "前端产物已复制到 $STATIC_DIR" ls -la "$STATIC_DIR" else echo "警告:未找到前端构建产物 $FRONTEND_DIST,将仅构建后端" fi # 生成版本标签 RELEASE_VERSION="v1.0.0" BUILD_VERSION="v1.0.0.${DRONE_BUILD_NUMBER}" echo "RELEASE_VERSION=$RELEASE_VERSION" > /drone/src/.version.env echo "BUILD_VERSION=$BUILD_VERSION" >> /drone/src/.version.env echo "BUILD_DATE=$(TZ=Asia/Shanghai date +%Y%m%d)" >> /drone/src/.version.env echo "发布版本: $RELEASE_VERSION, 构建版本: $BUILD_VERSION" # 检测可用 CPU 核数,动态设置 Maven 并行度 CPU_CORES=$(nproc 2>/dev/null || echo 4) echo "检测到 $CPU_CORES 个 CPU 核心" if [ "$CPU_CORES" -ge 4 ]; then MVN_THREADS="2C" else MVN_THREADS="1C" fi echo "Maven 并行度: -T $MVN_THREADS" # Maven 打包(使用阿里云镜像 + 多线程并行编译 + 跳过测试 + 离线优先) mvn -B clean package \ -s /root/.m2/settings.xml \ -DskipTests \ -Dmaven.test.skip=true \ -T "$MVN_THREADS" \ --no-transfer-progress \ --fail-at-end JAR_FILE=$(ls par-api/target/*.jar | head -1) echo "构建成功!产物: $JAR_FILE ($(du -sh $JAR_FILE | cut -f1))" depends_on: - prepare-cache - frontend-build # ===================== Docker 镜像构建 ===================== # 参考 mediabot 优化:Drone 已在前序步骤完成编译(frontend-build + maven-build), # 此处使用 CI 精简 Dockerfile,仅将预构建 jar 打包进 JRE 镜像,无需在 Docker 内重新编译。 - name: build-docker-image image: 192.168.31.253/library/docker:cli volumes: - name: dockersock path: /var/run/docker.sock environment: DOCKER_BUILDKIT: "1" commands: - | echo "====== 开始构建 Docker 镜像 ======" source /drone/src/.version.env # main 分支用干净版本号,test 带 pipeline 号 if [ "$DRONE_BRANCH" = "main" ]; then APP_VERSION="$RELEASE_VERSION" else APP_VERSION="$BUILD_VERSION" fi echo "版本: $APP_VERSION" # 验证 jar 产物存在 JAR_FILE=$(ls par-api/target/*.jar 2>/dev/null | head -1) if [ -z "$JAR_FILE" ]; then echo "错误:未找到 jar 产物,maven-build 可能失败" exit 1 fi echo "jar 产物: $JAR_FILE ($(du -sh $JAR_FILE | cut -f1))" # 使用 CI 精简 Dockerfile:仅 COPY jar + 设置运行环境,秒级完成 docker build \ --build-arg BUILDKIT_INLINE_CACHE=1 \ --build-arg VERSION="$APP_VERSION" \ --build-arg BUILD_TIME="$(TZ=Asia/Shanghai date '+%Y-%m-%d %H:%M')" \ -t "$IMAGE_NAME:$BUILD_VERSION" \ -t "$IMAGE_NAME:latest" \ -f Dockerfile \ . echo "Docker 镜像构建完成。" docker images | grep "$IMAGE_NAME" depends_on: - maven-build when: branch: [test, main] # ===================== Test:推本地 Harbor ===================== - name: push-to-harbor image: 192.168.31.253/library/docker:cli network_mode: host volumes: - name: dockersock path: /var/run/docker.sock environment: LOCAL_HARBOR_REG: "192.168.31.253" LOCAL_HARBOR_PROJ: "yescent" LOCAL_REGISTRY_USER: from_secret: local_registry_user LOCAL_REGISTRY_PASSWORD: from_secret: local_registry_password commands: - | echo "====== 推送镜像到本地 Harbor ======" source /drone/src/.version.env LOCAL_IMAGE_FULL="$LOCAL_HARBOR_REG/$LOCAL_HARBOR_PROJ/$IMAGE_NAME" LOCAL_IMAGE_TAG="$LOCAL_IMAGE_FULL:$BUILD_VERSION" LOCAL_IMAGE_LATEST="$LOCAL_IMAGE_FULL:latest" docker tag "$IMAGE_NAME:$BUILD_VERSION" "$LOCAL_IMAGE_TAG" docker tag "$IMAGE_NAME:latest" "$LOCAL_IMAGE_LATEST" echo "$LOCAL_REGISTRY_PASSWORD" | docker login -u "$LOCAL_REGISTRY_USER" --password-stdin $LOCAL_HARBOR_REG docker push "$LOCAL_IMAGE_TAG" docker push "$LOCAL_IMAGE_LATEST" docker rmi "$LOCAL_IMAGE_TAG" "$LOCAL_IMAGE_LATEST" || true echo "====== Harbor 推送完成 ======" depends_on: - build-docker-image when: status: success branch: [test] # ===================== Test:部署到测试服务器 ===================== - name: deploy-test image: 192.168.31.253/library/alpine-openssh:latest environment: DEPLOY_IMAGE: "192.168.31.253/yescent/${DRONE_REPO_NAME}" APP_PORT: "9012" SSH_USER: from_secret: test_ssh_user SSH_HOST: from_secret: test_ssh_host SSH_KEY: from_secret: test_ssh_key commands: - | echo "====== 部署测试环境 ======" source /drone/src/.version.env mkdir -p ~/.ssh chmod 700 ~/.ssh # 使用 printf 确保换行符正确写入 printf '%s\n' "$SSH_KEY" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa # 如果私钥是 OpenSSH 新格式,尝试转换为 PEM 格式 ssh-keygen -p -m PEM -N "" -f ~/.ssh/id_rsa 2>/dev/null || true export IMG="$DEPLOY_IMAGE:$BUILD_VERSION" ssh -o StrictHostKeyChecking=no -o IdentitiesOnly=yes -i ~/.ssh/id_rsa $SSH_USER@$SSH_HOST " docker pull $IMG docker stop par-server 2>/dev/null || true docker rm -f par-server 2>/dev/null || true docker run -d --name par-server --restart always \\ --env-file /data/par/db.env \\ -p \${APP_PORT:-9012}:8080 \\ -v /data/par/logs:/app/logs \\ -v /data/par/static:/app/static \\ $IMG sleep 10 curl -s http://localhost:\${APP_PORT:-9012}/health && echo OK || echo FAIL " echo "====== 部署完成 ======" depends_on: - push-to-harbor when: status: success branch: [test] # ===================== Main:推本地 Harbor 并部署 ===================== - name: push-to-harbor-main image: 192.168.31.253/library/docker:cli network_mode: host volumes: - name: dockersock path: /var/run/docker.sock environment: LOCAL_HARBOR_REG: "192.168.31.253" LOCAL_HARBOR_PROJ: "yescent" LOCAL_REGISTRY_USER: from_secret: local_registry_user LOCAL_REGISTRY_PASSWORD: from_secret: local_registry_password commands: - | echo "====== 推送镜像到本地 Harbor (main) ======" source /drone/src/.version.env LOCAL_IMAGE_FULL="$LOCAL_HARBOR_REG/$LOCAL_HARBOR_PROJ/$IMAGE_NAME" LOCAL_IMAGE_TAG="$LOCAL_IMAGE_FULL:$BUILD_VERSION" LOCAL_IMAGE_LATEST="$LOCAL_IMAGE_FULL:latest" docker tag "$IMAGE_NAME:$BUILD_VERSION" "$LOCAL_IMAGE_TAG" docker tag "$IMAGE_NAME:latest" "$LOCAL_IMAGE_LATEST" echo "$LOCAL_REGISTRY_PASSWORD" | docker login -u "$LOCAL_REGISTRY_USER" --password-stdin $LOCAL_HARBOR_REG docker push "$LOCAL_IMAGE_TAG" docker push "$LOCAL_IMAGE_LATEST" docker rmi "$LOCAL_IMAGE_TAG" "$LOCAL_IMAGE_LATEST" || true echo "====== Harbor 推送完成 ======" depends_on: - build-docker-image when: status: success branch: [main] - name: deploy-main image: 192.168.31.253/library/alpine-openssh:latest environment: DEPLOY_IMAGE: "192.168.31.253/yescent/${DRONE_REPO_NAME}" APP_PORT: "9012" SSH_USER: from_secret: prod_ssh_user SSH_HOST: from_secret: prod_ssh_host SSH_KEY: from_secret: prod_ssh_key commands: - | echo "====== 部署生产环境 ======" source /drone/src/.version.env mkdir -p ~/.ssh chmod 700 ~/.ssh # 使用 printf 确保换行符正确写入 printf '%s\n' "$SSH_KEY" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa # 如果私钥是 OpenSSH 新格式,尝试转换为 PEM 格式 ssh-keygen -p -m PEM -N "" -f ~/.ssh/id_rsa 2>/dev/null || true export IMG="$DEPLOY_IMAGE:$BUILD_VERSION" ssh -o StrictHostKeyChecking=no -o IdentitiesOnly=yes -i ~/.ssh/id_rsa $SSH_USER@$SSH_HOST " docker pull $IMG docker stop par-server 2>/dev/null || true docker rm -f par-server 2>/dev/null || true docker run -d --name par-server --restart always \\ --env-file /data/par/db.env \\ -p \${APP_PORT:-9012}:8080 \\ -v /data/par/logs:/app/logs \\ -v /data/par/static:/app/static \\ $IMG sleep 10 curl -s http://localhost:\${APP_PORT:-9012}/health && echo OK || echo FAIL " echo "====== 部署完成 ======" depends_on: - push-to-harbor-main when: status: success branch: [main] # ===================== 构建报告 ===================== - name: cache-report image: 192.168.31.253/library/alpine:latest volumes: - name: maven-cache path: /host-cache commands: - | echo "====== 构建流程结束 ======" if [ -d "/host-cache/repository" ]; then CACHE_SIZE=$(du -sh /host-cache/repository 2>/dev/null | cut -f1 || echo "计算失败") echo "Maven缓存大小: $CACHE_SIZE" fi source /drone/src/.version.env 2>/dev/null if [ -n "$BUILD_VERSION" ]; then echo "构建版本: $BUILD_VERSION" fi echo "==============================" when: status: [success, failure] depends_on: - maven-build