Skip to content

Merge pull request #43 from Geniusay/master #42

Merge pull request #43 from Geniusay/master

Merge pull request #43 from Geniusay/master #42

Workflow file for this run

name: PromptoLab-UI CI/CD Pipeline
on:
push:
branches: [master, main, develop]
paths:
- 'prompto-lab-ui/**'
- '.github/workflows/ui-release.yml'
pull_request:
branches: [master, main]
paths:
- 'prompto-lab-ui/**'
- '.github/workflows/ui-release.yml'
env:
NODE_VERSION: '18'
REGISTRY: ${{ secrets.DOCKER_REPO }}
UI_DIR: 'prompto-lab-ui'
jobs:
# Job 1: 检测变更和代码质量检查
code-quality:
name: 前端代码质量检查
runs-on: ubuntu-latest
if: contains(github.event.head_commit.message, '<Auto>') || github.event_name == 'pull_request'
outputs:
should-deploy: ${{ steps.check.outputs.should-deploy }}
version: ${{ steps.extract.outputs.version }}
module: ${{ steps.extract.outputs.module }}
has-ui-changes: ${{ steps.changes.outputs.ui }}
steps:
- name: Checkout代码
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: 检测文件变更
id: changes
run: |
# 检查是否有前端文件变更
if git diff --name-only HEAD~1 HEAD | grep -q "^prompto-lab-ui/"; then
echo "ui=true" >> $GITHUB_OUTPUT
echo "检测到前端文件变更"
else
echo "ui=false" >> $GITHUB_OUTPUT
echo "未检测到前端文件变更,跳过前端部署"
fi
- name: 提取提交信息
id: extract
if: steps.changes.outputs.ui == 'true'
run: |
commit_message="${{ github.event.head_commit.message }}"
echo "提交信息: $commit_message"
# 默认值
version="$(date +%Y%m%d-%H%M%S)"
module="promptolab-ui"
skip_tests="false"
skip_build="false"
skip_deploy="false"
# 解析参数
if [[ "$commit_message" =~ -v:([^\ ]*) ]]; then
version="${BASH_REMATCH[1]}"
fi
if [[ "$commit_message" =~ -m:([^\ ]*) ]]; then
module="${BASH_REMATCH[1]}"
fi
if [[ "$commit_message" =~ -skip:([^\ ]*) ]]; then
skip_option="${BASH_REMATCH[1]}"
case $skip_option in
tests) skip_tests="true" ;;
build) skip_build="true" ;;
deploy) skip_deploy="true" ;;
all) skip_tests="true"; skip_build="true"; skip_deploy="true" ;;
esac
fi
echo "version=$version" >> $GITHUB_OUTPUT
echo "module=$module" >> $GITHUB_OUTPUT
echo "skip-tests=$skip_tests" >> $GITHUB_OUTPUT
echo "skip-build=$skip_build" >> $GITHUB_OUTPUT
echo "skip-deploy=$skip_deploy" >> $GITHUB_OUTPUT
- name: 检查是否应该部署
id: check
if: steps.changes.outputs.ui == 'true'
run: |
if [[ "${{ github.ref }}" == "refs/heads/master" ]] || [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
echo "should-deploy=true" >> $GITHUB_OUTPUT
else
echo "should-deploy=false" >> $GITHUB_OUTPUT
fi
- name: 设置Node.js
if: steps.changes.outputs.ui == 'true'
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
cache-dependency-path: '${{ env.UI_DIR }}/package-lock.json'
- name: 安装依赖
if: steps.changes.outputs.ui == 'true'
working-directory: ${{ env.UI_DIR }}
run: npm ci
- name: 代码格式检查
if: steps.changes.outputs.ui == 'true' && steps.extract.outputs.skip-tests != 'true'
working-directory: ${{ env.UI_DIR }}
run: |
# 如果有eslint配置
if [ -f ".eslintrc.js" ] || [ -f ".eslintrc.json" ]; then
npm run lint || echo "Lint检查完成"
fi
- name: 类型检查
if: steps.changes.outputs.ui == 'true' && steps.extract.outputs.skip-tests != 'true'
working-directory: ${{ env.UI_DIR }}
run: npm run type-check
- name: 单元测试
if: steps.changes.outputs.ui == 'true' && steps.extract.outputs.skip-tests != 'true'
working-directory: ${{ env.UI_DIR }}
run: |
# 如果有测试脚本
if npm run | grep -q "test"; then
npm run test || echo "测试完成"
else
echo "未配置测试脚本,跳过测试"
fi
# Job 2: 构建应用
build:
name: 构建前端应用
runs-on: ubuntu-latest
needs: code-quality
if: needs.code-quality.outputs.should-deploy == 'true' && needs.code-quality.outputs.has-ui-changes == 'true' && needs.code-quality.result == 'success'
steps:
- name: Checkout代码
uses: actions/checkout@v4
- name: 设置Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
cache-dependency-path: '${{ env.UI_DIR }}/package-lock.json'
- name: 安装依赖
working-directory: ${{ env.UI_DIR }}
run: npm ci
- name: 构建应用
working-directory: ${{ env.UI_DIR }}
run: npm run build
- name: 上传构建产物
uses: actions/upload-artifact@v4
with:
name: ui-dist-files
path: ${{ env.UI_DIR }}/dist/
retention-days: 1
# Job 3: 构建Docker镜像
docker-build:
name: 构建前端Docker镜像
runs-on: ubuntu-latest
needs: [code-quality, build]
if: needs.code-quality.outputs.should-deploy == 'true' && needs.code-quality.outputs.has-ui-changes == 'true' && needs.build.result == 'success'
steps:
- name: Checkout代码
uses: actions/checkout@v4
- name: 下载构建产物
uses: actions/download-artifact@v4
with:
name: ui-dist-files
path: ${{ env.UI_DIR }}/dist/
- name: 设置Docker Buildx
uses: docker/setup-buildx-action@v3
- name: 登录Docker Registry
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_REPO }}
password: ${{ secrets.DOCKER_PWD }}
- name: 构建并推送Docker镜像
uses: docker/build-push-action@v5
with:
context: ${{ env.UI_DIR }}
file: ${{ env.UI_DIR }}/Dockerfile
push: true
tags: |
${{ env.REGISTRY }}/${{ needs.code-quality.outputs.module }}:${{ needs.code-quality.outputs.version }}
${{ env.REGISTRY }}/${{ needs.code-quality.outputs.module }}:latest
cache-from: type=gha
cache-to: type=gha,mode=max
# Job 4: 部署到服务器
deploy:
name: 部署前端到服务器
runs-on: ubuntu-latest
needs: [code-quality, docker-build]
if: needs.code-quality.outputs.should-deploy == 'true' && needs.code-quality.outputs.has-ui-changes == 'true' && needs.docker-build.result == 'success'
environment: production
steps:
- name: 部署到服务器
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SERVER_ADDRESS }}
username: ${{ secrets.SERVER_USERNAME || 'root' }}
password: ${{ secrets.SERVER_PWD }}
port: ${{ secrets.SERVER_PORT || '22' }}
script: |
set -e
MODULE="${{ needs.code-quality.outputs.module }}"
VERSION="${{ needs.code-quality.outputs.version }}"
REGISTRY="${{ env.REGISTRY }}"
PORT="${{ secrets.UI_APP_PORT || '80' }}"
echo "开始部署前端 $MODULE:$VERSION"
# 拉取最新镜像
docker pull $REGISTRY/$MODULE:$VERSION
# 停止并删除旧容器
if [ "$(docker ps -q -f name=$MODULE)" ]; then
echo "停止旧容器..."
docker stop $MODULE
fi
if [ "$(docker ps -aq -f name=$MODULE)" ]; then
echo "删除旧容器..."
docker rm $MODULE
fi
# 启动新容器
echo "启动新容器..."
docker run -d \
--name $MODULE \
-p $PORT:80 \
--restart unless-stopped \
--label "version=$VERSION" \
--label "deployed-at=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
--label "type=frontend" \
$REGISTRY/$MODULE:$VERSION
# 等待容器启动
sleep 10
# 健康检查
if curl -f http://localhost:$PORT > /dev/null 2>&1; then
echo "✅ 前端部署成功!应用运行在端口 $PORT"
else
echo "❌ 前端健康检查失败"
exit 1
fi
# 清理未使用的镜像
docker image prune -f
echo "🎉 前端部署完成!"
# Job 5: 通知
notify:
name: 前端部署通知
runs-on: ubuntu-latest
needs: [code-quality, build, docker-build, deploy]
if: always() && needs.code-quality.outputs.should-deploy == 'true' && needs.code-quality.outputs.has-ui-changes == 'true'
steps:
- name: 发送通知
run: |
if [[ "${{ needs.deploy.result }}" == "success" ]]; then
echo "✅ 前端部署成功通知"
# 这里可以添加钉钉、企业微信等通知
else
echo "❌ 前端部署失败通知"
# 这里可以添加失败通知
fi