diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 876ba83..57feb91 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,39 +1,4 @@ -stages: - - deploy - -deploy-to-server: - stage: deploy - tags: - - pis-deploy-code # 使用指定标签的 runner - image: alpine:latest # 轻量级基础镜像 - before_script: - # 更换 Alpine 镜像源为阿里云(国内加速) - - sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories - # 安装 rsync - - apk add --no-cache rsync - # 确保目标目录存在(若不存在则创建,用于后续判断和备份逻辑的基础) - - mkdir -p /mnt/data/pis/python3/DV3Admin - script: - # --- 新增:备份逻辑 --- - # 定义变量 - - TARGET_DIR="/mnt/data/pis/python3/DV3Admin" - - BACKUP_DATE=$(date +%Y%m%d) - - BACKUP_DIR="${TARGET_DIR}_bak_${BACKUP_DATE}" - - # 检查目标目录是否为空或是否存在有效文件,若存在则进行备份 - # 使用 ls -A 检查目录下是否有非隐藏文件或所有文件(包括隐藏),如果有内容则备份 - - if [ -d "$TARGET_DIR" ] && [ "$(ls -A $TARGET_DIR)" ]; then - echo "备份文件至路径 $BACKUP_DIR"; - cp -a "$TARGET_DIR" "$BACKUP_DIR"; - mkdir -p "$TARGET_DIR"; - else - echo "没有文件需要部署或文件夹为空"; - fi - - # --- 新增:代码同步逻辑(排除特定文件) --- - # 修复:将多行合并为一行,避免反斜杠被当作文件名处理 - - rsync -avz --delete --exclude='.git/' --exclude='.gitignore' --exclude='.gitattributes' --exclude='.gitlab-ci.yml' ./ "$TARGET_DIR/" - - - echo "代码部署成功" +# Jenkins 接管 CI/CD,禁用 GitLab CI +workflow: rules: - - when: manual # 手动触发流水线 \ No newline at end of file + - when: never diff --git a/docs/superpowers/plans/2026-03-31-jenkins-cicd-implementation.md b/docs/superpowers/plans/2026-03-31-jenkins-cicd-implementation.md deleted file mode 100644 index c2c275c..0000000 --- a/docs/superpowers/plans/2026-03-31-jenkins-cicd-implementation.md +++ /dev/null @@ -1,323 +0,0 @@ -# Jenkins CI/CD Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** 为 PIS 项目创建 Jenkins Pipeline,实现前后端独立构建、镜像推送至 Harbor、Docker Compose 部署。 - -**Architecture:** 前后端分离 Pipeline 设计。Backend 和 Frontend 各自独立构建镜像、推送 Harbor、SSH 部署。两个 Pipeline 可独立触发、互不影响。 - -**Tech Stack:** Jenkins Pipeline (Groovy), Docker, Harbor, Docker Compose, SSH - ---- - -## File Structure - -``` -├── backend/ -│ └── Jenkinsfile # 后端构建+部署Pipeline -├── web/ -│ └── Jenkinsfile # 前端构建+部署Pipeline -└── .gitlab-ci.yml # 禁用GitLab CI -``` - ---- - -## Task 1: Create Backend Jenkinsfile - -**Files:** -- Create: `backend/Jenkinsfile` - -- [ ] **Step 1: Create backend/Jenkinsfile** - -```groovy -pipeline { - agent any - - environment { - HARBOR_URL = 'harbor.example.com' - HARBOR_PROJECT = 'pis' - IMAGE_NAME = 'dvadmin3-django' - SERVER_HOST = 'your-server-ip' - COMPOSE_PATH = '/mnt/data/pis' - CREDENTIALS_ID = 'harbor-credentials' - SSH_CREDENTIALS_ID = 'server-ssh-key' - } - - stages { - stage('Checkout') { - steps { - checkout scm - } - } - - stage('Build Image') { - steps { - script { - def imageTag = "${HARBOR_URL}/${HARBOR_PROJECT}/${IMAGE_NAME}:${BUILD_NUMBER}" - env.IMAGE_TAG = imageTag - sh """ - docker build -f docker_env/django/Dockerfile \ - -t ${imageTag} . - """ - } - } - } - - stage('Push to Harbor') { - steps { - script { - withCredentials([usernamePassword( - credentialsId: CREDENTIALS_ID, - usernameVariable: 'HARBOR_USER', - passwordVariable: 'HARBOR_PASS' - )]) { - sh """ - echo ${HARBOR_PASS} | docker login ${HARBOR_URL} -u ${HARBOR_USER} --password-stdin - docker push ${IMAGE_TAG} - docker logout ${HARBOR_URL} - """ - } - } - } - } - - stage('Deploy') { - steps { - script { - withCredentials([sshUserPrivateKey( - credentialsId: SSH_CREDENTIALS_ID, - usernameVariable: 'SSH_USER', - privateKeyVariable: 'SSH_KEY' - )]) { - sh """ - ssh -o StrictHostKeyChecking=no ${SSH_USER}@${SERVER_HOST} << 'EOF' - cd ${COMPOSE_PATH} - docker-compose pull dvadmin3-django - docker-compose up -d --no-deps dvadmin3-django - EOF - """ - } - } - } - } - } - - post { - failure { - echo "Backend Pipeline failed! Check Jenkins logs." - } - success { - echo "Backend deployed successfully!" - } - } -} -``` - -- [ ] **Step 2: Commit backend/Jenkinsfile** - -```bash -git add backend/Jenkinsfile -git commit -m "feat(jenkins): add backend CI/CD pipeline" -``` - ---- - -## Task 2: Create Frontend Jenkinsfile - -**Files:** -- Create: `web/Jenkinsfile` - -- [ ] **Step 1: Create web/Jenkinsfile** - -```groovy -pipeline { - agent any - - environment { - HARBOR_URL = 'harbor.example.com' - HARBOR_PROJECT = 'pis' - IMAGE_NAME = 'dvadmin3-web' - SERVER_HOST = 'your-server-ip' - COMPOSE_PATH = '/mnt/data/pis' - CREDENTIALS_ID = 'harbor-credentials' - SSH_CREDENTIALS_ID = 'server-ssh-key' - } - - stages { - stage('Checkout') { - steps { - checkout scm - } - } - - stage('Install & Build') { - steps { - sh """ - cd web - yarn install --registry=https://registry.npmmirror.com - yarn build - """ - } - } - - stage('Build Image') { - steps { - script { - def imageTag = "${HARBOR_URL}/${HARBOR_PROJECT}/${IMAGE_NAME}:${BUILD_NUMBER}" - env.IMAGE_TAG = imageTag - sh """ - docker build -f docker_env/web/Dockerfile \ - -t ${imageTag} . - """ - } - } - } - - stage('Push to Harbor') { - steps { - script { - withCredentials([usernamePassword( - credentialsId: CREDENTIALS_ID, - usernameVariable: 'HARBOR_USER', - passwordVariable: 'HARBOR_PASS' - )]) { - sh """ - echo ${HARBOR_PASS} | docker login ${HARBOR_URL} -u ${HARBOR_USER} --password-stdin - docker push ${IMAGE_TAG} - docker logout ${HARBOR_URL} - """ - } - } - } - } - - stage('Deploy') { - steps { - script { - withCredentials([sshUserPrivateKey( - credentialsId: SSH_CREDENTIALS_ID, - usernameVariable: 'SSH_USER', - privateKeyVariable: 'SSH_KEY' - )]) { - sh """ - ssh -o StrictHostKeyChecking=no ${SSH_USER}@${SERVER_HOST} << 'EOF' - cd ${COMPOSE_PATH} - docker-compose pull dvadmin3-web - docker-compose up -d --no-deps dvadmin3-web - EOF - """ - } - } - } - } - } - - post { - failure { - echo "Frontend Pipeline failed! Check Jenkins logs." - } - success { - echo "Frontend deployed successfully!" - } - } -} -``` - -- [ ] **Step 2: Commit web/Jenkinsfile** - -```bash -git add web/Jenkinsfile -git commit -m "feat(jenkins): add frontend CI/CD pipeline" -``` - ---- - -## Task 3: Disable GitLab CI - -**Files:** -- Modify: `.gitlab-ci.yml` - -- [ ] **Step 1: Modify .gitlab-ci.yml to disable GitLab CI** - -```yaml -# Jenkins 接管 CI/CD,禁用 GitLab CI -workflow: - rules: - - when: never -``` - -- [ ] **Step 2: Commit .gitlab-ci.yml change** - -```bash -git add .gitlab-ci.yml -git commit -m "chore: disable GitLab CI, use Jenkins instead" -``` - ---- - -## Task 4: Final Verification - -- [ ] **Step 1: Verify all files created** - -```bash -git status -``` - -Expected output: -``` -new file: backend/Jenkinsfile -new file: web/Jenkinsfile -modified: .gitlab-ci.yml -``` - ---- - -## Jenkins Configuration Summary (Manual Steps Required) - -These steps must be done manually in Jenkins UI: - -### 1. Add Credentials - -**Harbor Credentials:** -- Type: Username with Password -- ID: `harbor-credentials` -- Username: (your Harbor username) -- Password: (your Harbor password) - -**SSH Credentials:** -- Type: SSH Username with Private Key -- ID: `server-ssh-key` -- Username: (server SSH user) -- Private Key: (server SSH private key) - -### 2. Create Backend Job -- New Item → Pipeline -- Name: `pis-backend` -- Branch: `*/develop` -- Script Path: `backend/Jenkinsfile` - -### 3. Create Frontend Job -- New Item → Pipeline -- Name: `pis-web` -- Branch: `*/develop` -- Script Path: `web/Jenkinsfile` - -### 4. Update Pipeline Variables - -In each Jenkinsfile, update these placeholder values: -```groovy -HARBOR_URL = 'harbor.example.com' // 替换为实际Harbor地址 -HARBOR_PROJECT = 'pis' // 替换为实际项目名 -SERVER_HOST = 'your-server-ip' // 替换为实际服务器IP -``` - ---- - -## Spec Coverage Checklist - -- [x] Backend Jenkinsfile (build, push, deploy) -- [x] Frontend Jenkinsfile (build, push, deploy) -- [x] GitLab CI disabled -- [x] Harbor push integration -- [x] SSH deployment -- [x] Manual Jenkins configuration guide diff --git a/docs/superpowers/specs/2026-03-31-jenkins-cicd-design.md b/docs/superpowers/specs/2026-03-31-jenkins-cicd-design.md deleted file mode 100644 index 78e5b74..0000000 --- a/docs/superpowers/specs/2026-03-31-jenkins-cicd-design.md +++ /dev/null @@ -1,335 +0,0 @@ -# Jenkins CI/CD Pipeline 设计 - -## 概述 - -为 PIS 采购询报价管理系统配置 Jenkins 持续集成/持续部署,实现前后端独立构建、镜像推送至 Harbor、私有服务器 Docker Compose 部署。 - -## 技术栈 - -| 组件 | 技术 | -|------|------| -| 后端 | Django 4.2 + DRF + uWSGI | -| 前端 | Node.js + Vite + TypeScript | -| 数据库 | MySQL 8.0 | -| 队列 | Redis 6.2 + Celery | -| 镜像仓库 | Harbor (私有) | -| 部署方式 | Docker Compose | - -## 架构设计 - -``` -GitLab (develop branch) - │ - ▼ -┌─────────────────────────────────────┐ -│ Jenkins Master │ -├─────────────────────────────────────┤ -│ ┌──────────────┐ ┌──────────────┐ │ -│ │ Backend Jenkinsfile │ │Web Jenkinsfile │ │ -│ └───────┬───────┘ └───────┬──────┘ │ -│ │ │ │ -│ ▼ ▼ │ -│ ┌──────────┐ ┌──────────┐ │ -│ │ Build & │ │ Build & │ │ -│ │ Push to │ │ Push to │ │ -│ │ Harbor │ │ Harbor │ │ -│ └────┬─────┘ └────┬─────┘ │ -│ └────────┬────────┘ │ -│ ▼ │ -│ Docker Compose │ -│ 远程服务器 │ -└─────────────────────────────────────┘ -``` - -## Pipeline 设计 - -### 1. Backend Pipeline (`backend/Jenkinsfile`) - -#### 环境变量 - -| 变量 | 说明 | 示例值 | -|------|------|--------| -| HARBOR_URL | Harbor 仓库地址 | harbor.example.com | -| HARBOR_PROJECT | Harbor 项目名 | pis | -| IMAGE_NAME | 镜像名 | dvadmin3-django | -| SERVER_HOST | 部署服务器IP | 192.168.1.100 | -| COMPOSE_PATH | docker-compose 路径 | /mnt/data/pis | - -#### 构建阶段 - -| Stage | 步骤 | 命令 | -|-------|------|------| -| Checkout | 拉取后端代码 | `git clone` | -| Build | 构建 Django 镜像 | `docker build -f docker_env/django/Dockerfile -t ${IMAGE_TAG}` | -| Push | 推送镜像到 Harbor | `docker push ${IMAGE_TAG}` | -| Deploy | SSH 远程部署 | `ssh ${SERVER_HOST} "cd ${COMPOSE_PATH} && docker-compose pull && docker-compose up -d --no-deps dvadmin3-django"` | - -#### Jenkinsfile 模板 - -```groovy -pipeline { - agent any - - environment { - HARBOR_URL = 'harbor.example.com' - HARBOR_PROJECT = 'pis' - IMAGE_NAME = 'dvadmin3-django' - SERVER_HOST = 'your-server-ip' - COMPOSE_PATH = '/mnt/data/pis' - CREDENTIALS_ID = 'harbor-credentials' - SSH_CREDENTIALS_ID = 'server-ssh-key' - } - - stages { - stage('Checkout') { - steps { - checkout scm - } - } - - stage('Build Image') { - steps { - script { - def imageTag = "${HARBOR_URL}/${HARBOR_PROJECT}/${IMAGE_NAME}:${BUILD_NUMBER}" - env.IMAGE_TAG = imageTag - sh """ - docker build -f docker_env/django/Dockerfile \ - -t ${imageTag} . - """ - } - } - } - - stage('Push to Harbor') { - steps { - script { - withCredentials([usernamePassword( - credentialsId: CREDENTIALS_ID, - usernameVariable: 'HARBOR_USER', - passwordVariable: 'HARBOR_PASS' - )]) { - sh """ - echo ${HARBOR_PASS} | docker login ${HARBOR_URL} -u ${HARBOR_USER} --password-stdin - docker push ${IMAGE_TAG} - docker logout ${HARBOR_URL} - """ - } - } - } - } - - stage('Deploy') { - steps { - script { - withCredentials([sshUserPrivateKey( - credentialsId: SSH_CREDENTIALS_ID, - usernameVariable: 'SSH_USER', - privateKeyVariable: 'SSH_KEY' - )]) { - sh """ - ssh -o StrictHostKeyChecking=no ${SSH_USER}@${SERVER_HOST} """ - cd ${COMPOSE_PATH} && \\ - docker-compose pull dvadmin3-django && \\ - docker-compose up -d --no-deps dvadmin3-django - """ - """ - } - } - } - } - } - - post { - failure { - echo "Pipeline failed! Check Jenkins logs." - } - } -} -``` - -### 2. Frontend Pipeline (`web/Jenkinsfile`) - -#### 构建阶段 - -| Stage | 步骤 | 命令 | -|-------|------|------| -| Checkout | 拉取前端代码 | `git clone` | -| Install | 安装依赖 | `npm install` | -| Build | 构建前端产物 | `npm run build` | -| Build Image | 构建 Nginx 镜像 | `docker build -f docker_env/web/Dockerfile -t ${IMAGE_TAG}` | -| Push | 推送镜像到 Harbor | `docker push ${IMAGE_TAG}` | -| Deploy | SSH 远程部署 | `ssh ${SERVER_HOST} "docker-compose up -d --no-deps dvadmin3-web"` | - -#### Jenkinsfile 模板 - -```groovy -pipeline { - agent any - - environment { - HARBOR_URL = 'harbor.example.com' - HARBOR_PROJECT = 'pis' - IMAGE_NAME = 'dvadmin3-web' - SERVER_HOST = 'your-server-ip' - COMPOSE_PATH = '/mnt/data/pis' - CREDENTIALS_ID = 'harbor-credentials' - SSH_CREDENTIALS_ID = 'server-ssh-key' - } - - stages { - stage('Checkout') { - steps { - checkout scm - } - } - - stage('Install & Build') { - steps { - sh """ - npm install - npm run build - """ - } - } - - stage('Build Image') { - steps { - script { - def imageTag = "${HARBOR_URL}/${HARBOR_PROJECT}/${IMAGE_NAME}:${BUILD_NUMBER}" - env.IMAGE_TAG = imageTag - sh """ - docker build -f docker_env/web/Dockerfile \ - -t ${imageTag} . - """ - } - } - } - - stage('Push to Harbor') { - steps { - script { - withCredentials([usernamePassword( - credentialsId: CREDENTIALS_ID, - usernameVariable: 'HARBOR_USER', - passwordVariable: 'HARBOR_PASS' - )]) { - sh """ - echo ${HARBOR_PASS} | docker login ${HARBOR_URL} -u ${HARBOR_USER} --password-stdin - docker push ${IMAGE_TAG} - docker logout ${HARBOR_URL} - """ - } - } - } - } - - stage('Deploy') { - steps { - script { - withCredentials([sshUserPrivateKey( - credentialsId: SSH_CREDENTIALS_ID, - usernameVariable: 'SSH_USER', - privateKeyVariable: 'SSH_KEY' - )]) { - sh """ - ssh -o StrictHostKeyChecking=no ${SSH_USER}@${SERVER_HOST} """ - cd ${COMPOSE_PATH} && \\ - docker-compose pull dvadmin3-web && \\ - docker-compose up -d --no-deps dvadmin3-web - """ - """ - } - } - } - } - } - - post { - failure { - echo "Pipeline failed! Check Jenkins logs." - } - } -} -``` - -## Jenkins 配置要求 - -### 1. 凭证配置 - -#### Harbor 凭证 -- 类型:Username with Password -- ID:`harbor-credentials` -- 用户名/密码:Harbor 仓库账号 - -#### SSH 凭证 -- 类型:SSH Username with Private Key -- ID:`server-ssh-key` -- 用户名:服务器 SSH 用户 -- 私钥:服务器 SSH 私钥 - -### 2. Jenkins Job 配置 - -#### Backend Job -- 类型:Pipeline -- 源码管理:Git -- Repository URL:项目 Git 地址 -- Branch:*/develop -- Script Path:backend/Jenkinsfile - -#### Frontend Job -- 类型:Pipeline -- 源码管理:Git -- Repository URL:项目 Git 地址 -- Branch:*/develop -- Script Path:web/Jenkinsfile - -### 3. Docker 配置 - -Jenkins Agent 需要: -- Docker CLI 已安装 -- Docker daemon 正常运行 -- 可访问 Harbor 仓库 - -## Web Dockerfile 要求 - -需要在 `web/docker_env/web/Dockerfile` 创建 Nginx 镜像: - -```dockerfile -FROM nginx:alpine -COPY dist/ /usr/share/nginx/html/ -COPY docker_env/nginx/conf.d /etc/nginx/conf.d/ -EXPOSE 80 -CMD ["nginx", "-g", "daemon off;"] -``` - -## GitLab CI 禁用 - -在 `.gitlab-ci.yml` 开头添加: - -```yaml -# Jenkins 接管 CI/CD,禁用 GitLab CI -workflow: - rules: - - when: never -``` - -或者直接删除 `.gitlab-ci.yml` 文件。 - -## 部署流程 - -1. 开发者 push 代码到 `develop` 分支 -2. Jenkins 自动触发 Pipeline -3. 前后端独立构建镜像 -4. 镜像推送到 Harbor -5. SSH 登录部署服务器 -6. 执行 `docker-compose pull` 拉取新镜像 -7. 执行 `docker-compose up -d` 重启服务 -8. Jenkins Job 显示成功/失败状态 - -## 未来扩展 - -- [ ] 添加单元测试阶段 -- [ ] 添加 SonarQube 代码审查 -- [ ] 添加钉钉/企业微信通知 -- [ ] 添加镜像版本回滚功能 -- [ ] 配置 Blue-Green Deployment