Skip to content

Merge pull request #32 from yyyCode/feat/idempotent-framework #165

Merge pull request #32 from yyyCode/feat/idempotent-framework

Merge pull request #32 from yyyCode/feat/idempotent-framework #165

Workflow file for this run

name: Build and deploy OpenBlog
on:
workflow_dispatch:
push:
branches:
- master
paths-ignore:
- '**/*.md'
jobs:
# 检测变更范围。business / message / gateway 三条链互不影响;共享契约(OpenBlog-api/**)变更会同时触发 business 与 message 双链。
changes:
runs-on: ubuntu-latest
outputs:
business: ${{ steps.filter.outputs.business }}
message: ${{ steps.filter.outputs.message }}
gateway: ${{ steps.filter.outputs.gateway }}
frontend: ${{ steps.filter.outputs.frontend }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
business:
- 'OpenBlog-business/**'
- 'OpenBlog-framework*/**'
- 'OpenBlog-common/**'
- 'OpenBlog-api/**'
- 'deploy/business/**'
- 'pom.xml'
message:
- 'OpenBlog-message/**'
- 'OpenBlog-common/**'
- 'OpenBlog-api/**'
- 'deploy/message/**'
- 'pom.xml'
gateway:
- 'OpenBlog-gateway/**'
- 'OpenBlog-common/**'
- 'OpenBlog-framework*/**'
- 'deploy/gateway/**'
- 'pom.xml'
frontend:
- 'vue/**'
# ============ business 链(独立) ============
build-business:
needs: changes
if: ${{ needs.changes.outputs.business == 'true' || github.event_name == 'workflow_dispatch' }}
runs-on: ubuntu-latest
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: "17"
distribution: "temurin"
cache: "maven"
- name: Build business (Maven)
run: mvn -B -ntp package -DskipTests -pl OpenBlog-business -am
- name: Upload business JAR
uses: actions/upload-artifact@v4
with:
name: business-jar
path: OpenBlog-business/target/OpenBlog-business-*.jar
retention-days: 1
deploy-business:
needs: build-business
if: ${{ needs.build-business.result == 'success' || github.event_name == 'workflow_dispatch' }}
runs-on: openblog-backend
steps:
- name: Checkout(获取 deploy/business 下的 Docker 部署文件)
uses: actions/checkout@v4
- name: Download business JAR
uses: actions/download-artifact@v4
with:
name: business-jar
path: business
- name: Deploy via Docker(构建镜像 + 重启容器 + 注入 JWT secret)
env:
OPENBLOG_JWT_SECRET: ${{ secrets.OPENBLOG_JWT_SECRET }}
run: |
set -euo pipefail
TARGET_DIR="/www/wwwroot/java/openblog"
TARGET_JAR="$TARGET_DIR/OpenBlog-business-1.0.0-SNAPSHOT.jar"
# 1. 停掉旧 systemd 服务(释放 8082,避免与容器抢端口)
systemctl stop openblog 2>/dev/null || true
systemctl disable openblog 2>/dev/null || true
# 2. 备份旧 jar
if [ -f "$TARGET_JAR" ]; then
mkdir -p "$TARGET_DIR/backup"
cp "$TARGET_JAR" "$TARGET_DIR/backup/OpenBlog-business-$(date +%Y%m%d-%H%M%S).jar" 2>/dev/null || true
fi
# 3.5 写入 .env(compose 自动加载),JWT secret 与 gateway 同值
printf 'OPENBLOG_JWT_SECRET=%s\n' "$OPENBLOG_JWT_SECRET" > "$TARGET_DIR/.env"
# 3. 拷贝新 jar + Docker 部署文件
JAR_FILE=$(ls business/OpenBlog-business-*.jar | head -1)
cp "$JAR_FILE" "$TARGET_JAR"
cp deploy/business/Dockerfile deploy/business/docker-compose.yml deploy/business/.dockerignore "$TARGET_DIR/"
# 4. Docker 构建并启动容器
cd "$TARGET_DIR"
docker info >/dev/null 2>&1 || { echo "!! Docker 守护进程未运行,请先启动 Docker" >&2; exit 1; }
if docker compose version >/dev/null 2>&1; then
DC="docker compose"
elif docker-compose version >/dev/null 2>&1; then
DC="docker-compose"
else
echo "!! 服务器上没有 docker compose / docker-compose" >&2
exit 1
fi
echo "==> 使用: $DC"
$DC up -d --build --remove-orphans
$DC ps
echo "==> Deployed OpenBlog-business via Docker"
# ============ message 链(独立,与 business 互不影响) ============
build-message:
needs: changes
if: ${{ needs.changes.outputs.message == 'true' || github.event_name == 'workflow_dispatch' }}
runs-on: ubuntu-latest
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: "17"
distribution: "temurin"
cache: "maven"
- name: Build message (Maven)
run: mvn -B -ntp package -DskipTests -pl OpenBlog-message -am
- name: Upload message JAR
uses: actions/upload-artifact@v4
with:
name: message-jar
path: OpenBlog-message/target/OpenBlog-message-*.jar
retention-days: 1
deploy-message:
needs: build-message
if: ${{ needs.build-message.result == 'success' || github.event_name == 'workflow_dispatch' }}
runs-on: openblog-backend
steps:
- name: Checkout(获取 deploy/message 下的 Docker 部署文件)
uses: actions/checkout@v4
- name: Download message JAR
uses: actions/download-artifact@v4
with:
name: message-jar
path: message
- name: Deploy via Docker(构建镜像 + 重启容器 + 注入阿里云凭据)
env:
ALIYUN_AK: ${{ secrets.ALIYUN_AK }}
ALIYUN_SK: ${{ secrets.ALIYUN_SK }}
ALIYUN_FROM: ${{ secrets.ALIYUN_FROM }}
run: |
set -euo pipefail
TARGET_DIR="/www/wwwroot/java/openblog-message"
TARGET_JAR="$TARGET_DIR/OpenBlog-message-1.0.0-SNAPSHOT.jar"
# 1. 停掉旧 宝塔/手动部署的 message 进程(释放 8083/20883;若无则该步 no-op)
systemctl stop openblog-message 2>/dev/null || true
# 2. 备份旧 jar
mkdir -p "$TARGET_DIR"
if [ -f "$TARGET_JAR" ]; then
mkdir -p "$TARGET_DIR/backup"
cp "$TARGET_JAR" "$TARGET_DIR/backup/OpenBlog-message-$(date +%Y%m%d-%H%M%S).jar" 2>/dev/null || true
fi
# 3. 拷贝新 jar + Docker 部署文件
JAR_FILE=$(ls message/OpenBlog-message-*.jar | head -1)
cp "$JAR_FILE" "$TARGET_JAR"
cp deploy/message/Dockerfile deploy/message/docker-compose.yml deploy/message/.dockerignore "$TARGET_DIR/"
# 4. 阿里云凭据写入服务器 .env(compose 自动加载),供 message 容器读取
printf 'ALIYUN_AK=%s\nALIYUN_SK=%s\nALIYUN_FROM=%s\n' \
"$ALIYUN_AK" "$ALIYUN_SK" "$ALIYUN_FROM" \
> "$TARGET_DIR/.env"
# 5. Docker 构建并启动容器
cd "$TARGET_DIR"
docker info >/dev/null 2>&1 || { echo "!! Docker 守护进程未运行,请先启动 Docker" >&2; exit 1; }
if docker compose version >/dev/null 2>&1; then
DC="docker compose"
elif docker-compose version >/dev/null 2>&1; then
DC="docker-compose"
else
echo "!! 服务器上没有 docker compose / docker-compose" >&2
exit 1
fi
echo "==> 使用: $DC"
$DC up -d --build --remove-orphans
$DC ps
echo "==> Deployed OpenBlog-message via Docker"
# ============ gateway 链(独立,与 business/message 互不影响) ============
build-gateway:
needs: changes
if: ${{ needs.changes.outputs.gateway == 'true' || github.event_name == 'workflow_dispatch' }}
runs-on: ubuntu-latest
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: "17"
distribution: "temurin"
cache: "maven"
- name: Build gateway (Maven)
run: mvn -B -ntp package -pl OpenBlog-gateway -am
- name: Upload gateway JAR
uses: actions/upload-artifact@v4
with:
name: gateway-jar
path: OpenBlog-gateway/target/OpenBlog-gateway-*.jar
retention-days: 1
deploy-gateway:
needs: build-gateway
if: ${{ needs.build-gateway.result == 'success' || github.event_name == 'workflow_dispatch' }}
runs-on: openblog-backend
steps:
- name: Checkout(获取 deploy/gateway 下的 Docker 部署文件)
uses: actions/checkout@v4
- name: Download gateway JAR
uses: actions/download-artifact@v4
with:
name: gateway-jar
path: gateway
- name: Deploy via Docker(构建镜像 + 重启容器 + 注入 JWT secret)
env:
OPENBLOG_JWT_SECRET: ${{ secrets.OPENBLOG_JWT_SECRET }}
run: |
set -euo pipefail
TARGET_DIR="/www/wwwroot/java/openblog-gateway"
TARGET_JAR="$TARGET_DIR/OpenBlog-gateway-1.0.0-SNAPSHOT.jar"
# 1. 停掉旧进程(若有,释放 8090)
systemctl stop openblog-gateway 2>/dev/null || true
# 2. 备份旧 jar
mkdir -p "$TARGET_DIR"
if [ -f "$TARGET_JAR" ]; then
mkdir -p "$TARGET_DIR/backup"
cp "$TARGET_JAR" "$TARGET_DIR/backup/OpenBlog-gateway-$(date +%Y%m%d-%H%M%S).jar" 2>/dev/null || true
fi
# 3. 拷贝新 jar + Docker 部署文件
JAR_FILE=$(ls gateway/OpenBlog-gateway-*.jar | head -1)
cp "$JAR_FILE" "$TARGET_JAR"
cp deploy/gateway/Dockerfile deploy/gateway/docker-compose.yml deploy/gateway/.dockerignore "$TARGET_DIR/"
# 4. JWT secret 写入服务器 .env(compose 自动加载)
printf 'OPENBLOG_JWT_SECRET=%s\n' "$OPENBLOG_JWT_SECRET" > "$TARGET_DIR/.env"
# 5. Docker 构建并启动容器
cd "$TARGET_DIR"
docker info >/dev/null 2>&1 || { echo "!! Docker 守护进程未运行,请先启动 Docker" >&2; exit 1; }
if docker compose version >/dev/null 2>&1; then
DC="docker compose"
elif docker-compose version >/dev/null 2>&1; then
DC="docker-compose"
else
echo "!! 服务器上没有 docker compose / docker-compose" >&2
exit 1
fi
echo "==> 使用: $DC"
$DC up -d --build --remove-orphans
$DC ps
echo "==> Deployed OpenBlog-gateway via Docker"
# ============ 前端链(原样保留) ============
build-frontend:
needs: changes
if: ${{ needs.changes.outputs.frontend == 'true' || github.event_name == 'workflow_dispatch' }}
runs-on: ubuntu-latest
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "22"
cache: "npm"
cache-dependency-path: vue/package-lock.json
- name: Install frontend dependencies and build
working-directory: vue
run: |
npm ci
npm run build
- name: Prepare release bundle for server
run: |
mkdir -p release
cp -r vue/dist release/dist
cp vue/Dockerfile vue/docker.sh vue/nginx.conf release/
- name: Build Docker image on runner
run: |
docker build -t openblog-web release/
docker save openblog-web | gzip > release/openblog-web.tar.gz
- name: Upload release bundle
uses: actions/upload-artifact@v4
with:
name: release-frontend
path: release/
deploy-frontend:
needs: build-frontend
if: ${{ needs.build-frontend.result == 'success' }}
runs-on: ubuntu-latest
steps:
- name: Download release bundle
uses: actions/download-artifact@v4
with:
name: release-frontend
path: release/
- name: Deploy files to server
uses: easingthemes/ssh-deploy@v5.1.1
with:
SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }}
ARGS: "-rlgoDzvc --delete --exclude=dist/.user.ini"
SOURCE: "release/dist release/Dockerfile release/docker.sh release/nginx.conf release/openblog-web.tar.gz"
REMOTE_HOST: ${{ secrets.SERVER_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.SERVER_REMOTE_USER }}
TARGET: ${{ secrets.SERVER_TARGET }}
SCRIPT_BEFORE: ls -la
- name: Run Docker commands on server
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.SERVER_REMOTE_HOST }}
username: ${{ secrets.SERVER_REMOTE_USER }}
key: ${{ secrets.SERVER_SSH_KEY }}
command_timeout: 30m
script: |
cd ${{ secrets.SERVER_TARGET }}
sh docker.sh