Jenkins+Gitea搭建CI/CD自动化+代码迁移(离线环境)

基础环境信息

服务器ip:

192.168.253.129

192.168.253.137

环境:Kylin v10 Jenkins:jenkins:2.541.2-lts Gitea:gitea:1.21.4 Nodejs:v20.19.0 yarn:yarn-v1.22.22 jdk:jdk-17.0.9 docker、docker-compose ​ 注:由于是离线环境、所以以上都需要准备好二进制安装包 或者从有网环境导出docker镜像、下面会提供一个/etc/docker/daemon.json国内镜像加速json {  "registry-mirrors": [    "https://docker.mirrors.ustc.edu.cn",    "https://hub-mirror.c.163.com" ],  "insecure-registries": [],  "exec-opts": ["native.cgroupdriver=systemd"],  "log-driver": "json-file",  "log-opts": {    "max-size": "100m" },  "storage-driver": "overlay2" }

1.环境准备

192.168.253.129 项目根目录:/app jenkins映射目录:/usr/docker/jenkins_data ​ #时间同步 ntpdate ntp.aliyun.com ​ #jdk17安装 cd /app tar xf ibm-semeru-open-jdk_x64_linux_17.0.9_9_openj9-0.41.0.tar.gz vim /etc/profile export JAVA_HOME=/app/jdk-17.0.9 export PATH=$JAVA_HOME/bin:$PATH source /etc/profile java -version验证#docker、jenkins安装 官网安装 docker-24.0.9.tgz tar -xvf docker-24.0.9.tgz cp docker/* /usr/bin/ vi /etc/systemd/system/docker.service systemctl daemon-reload systemctl start docker systemctl enable docker ​ ​ docker run -d   -p 8082:8080   -p 50000:50000   --dns 8.8.8.8   --dns 114.114.114.114   -v /usr/docker/jenkins_data:/var/jenkins_home   -v /etc/localtime:/etc/localtime:ro   -v /usr/bin/docker:/usr/bin/docker   -v /var/run/docker.sock:/var/run/docker.sock   -e JAVA_OPTS="-Duser.timezone=Asia/Shanghai"   --restart=on-failure   -u 0   --name myjenkins   jenkins/jenkins:2.541.2-lts ​ docker logs查看密码 ​ #我事先在有网环境,把需要的插件都安装了,然后直接把plugins目录打包,拷贝到离线环境对应的目录中即可 unzip plugins.zip -d /usr/docker/jenkins_data/plugins/ ​ #这里跑起来之后就可以访问ip:8082访问了 进去之后不装自定义插件 直接跳过 配置需要等maven node这些装好了在去网页调#maven安装 cd /app/ mkdir maven cd maven && tar xf apache-maven-3.9.12-bin.tar.gz \cp -r /app/maven/apache-maven-3.9.12 /usr/docker/jenkins_data/ ​ vim /etc/profile export MAVEN_HOME=/app/maven/apache-maven-3.9.12 export PATH=${MAVEN_HOME}/bin:${PATH} ​ source /etc/profile vi /usr/docker/jenkins_data/apache-maven-3.9.12/conf/settings.xml <!-- localRepository   | The path to the local repository maven will use to store artifacts.   |   | Default: ${user.home}/.m2/repository <localRepository>/path/to/local/repo</localRepository>  --> <localRepository>/var/jenkins_home/maven_repository</localRepository> ​ ##由于是离线环境 这里需要先在本地把后端代码maven一遍得到repository文件夹 然后zip过来 电脑目录:C:\Users\pc2\.m2\repository cd /usr/docker/jenkins_data/ && mkdir maven_repository && cd maven_repository unzip repository.zip mv repository maven_repository#nodejs、yarn安装 node_modules迁移 cd /app tar xf node-v20.19.0-linux-x64.tar.gz mv node-v20.19.0-linux-x64 node-v20.19.0 vi /etc/profile export NODE_HOME=/app/node-v20.19.0 export PATH=$NODE_HOME/bin:$PATH source /etc/profile node -v npm -v cp -r /app/node-v20.19.0 /usr/docker/jenkins_data/ ​ #node_modules 需要在本地电脑 npm运行一次然后将这个目录zip cd /usr/docker/jenkins_data/ tar xf yarn-v1.22.22.tar.gz docker exec -it -u root myjenkins ln -s /var/jenkins_home/yarn-tool/bin/yarn /usr/local/bin/yarn docker exec -it myjenkins yarn -v docker exec -it -u root myjenkins ln -s /var/jenkins_home/node-v20.19.0/bin/node /usr/local/bin/node docker exec -it -u root myjenkins ln -s /var/jenkins_home/node-v20.19.0/bin/npm /usr/local/bin/npm docker exec -it myjenkins yarn -v mkdir -p /usr/docker/jenkins_data/frontend_cache/node_modules cd frontend_cache/node_modules/ unzip node_modules.zip

2.jenkins配置

#全局工具配置 #别名自定义 无要求

#生成git服务器的公钥 #需要在jenkins服务器执行这个命令 并且ssh端口需要宿主机真实端口 [root@Kylin01 plugins]# ssh-keyscan -p 222 -t rsa 192.168.253.137 # 192.168.253.137:222 SSH-2.0-OpenSSH_9.3 [192.168.253.137]:222 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCpfG5+2lizKDAhCXMXNvmYpRrloXCENzxwihKCWJ7HcfnLHNa+nTGCmmKbqYybn6XB63xeXAxUwj+Al1kHk9mDV28LOQ5o9sFPPKgE0vdHbvSvYTsYxgThFHR3XLnUj/TAasTZLPDkTQo0IP5PeKavy63B5RIIGyd0LH3NgiHbM3LyEr9mjuNr+tbygNrHkCjIye8bT/S8zp5q3y7kclLMUynCLJNEydefqITj2rNU2IqKYoWumCB1G9sFQbKLuPlqu6TU41zJ4n1lQEe/JcCOl+BhJIxcJ3OhEee8dbLYfiuqx3UQGbEswjSSfKZVQb/AcyWZu/1uytNLBaRDKOfm/lcjunQhbaPPIpt2TeYkTgLn3yfPrzQ2S1TJcTI4+cyn0e7teWdMlGOSCCkXCm0bSl9qk1IKeaMMHJ94aclMd6lWBn8qDgwKOtyvJfRDYeZTcv7Ai6YZRYwGH3W81RIg+hBoJyrt0OaYVmLhMOO0rr6aHxz+TD633RWpBjz12Ic=

#设置凭据 这里获取的是jenkins服务器的私钥 同时后面gitea服务器也要设置对应同一套的公钥 [root@Kylin01 plugins]# docker exec -it myjenkins bash [root@Kylin01 plugins]# ssh-keygen -t rsa -b 4096 root@49413caa42b7:/# cat /root/.ssh/id_rsa root@49413caa42b7:/# cat /root/.ssh/id_rsa.pub

#流水线job配置 后端job的tigger配置

前端job的tigger配置 为了识别json

#后端Pipeline scripts ​ pipeline {   agent any ​   tools {       // 请确保在 Jenkins "Global Tool Configuration" 中配置了名为 'maven-3.9.12' 的 Maven       maven 'maven-3.9.12'   } ​   triggers {       pollSCM('')   } ​   environment {       // ================= 配置区 =================       // 1. 目标宿主机信息 (用于部署)       REMOTE_IP   = "192.168.253.137"       DEPLOY_PORT = "22"         // 宿主机 SSH 端口       REMOTE_USER = "root"       REMOTE_DIR  = "/app/xhdj_dev/project_backend"               // 2. Gitea 信息 (仅用于拉取代码)       GITEA_PORT  = "222"         // Gitea 容器映射端口       GITEA_CRED  = "gitea-ssh-key" // Jenkins 中配置的凭据 ID               // 3. 待发布的模块列表       WHITELIST   = "zhdq-auth-web zhdq-gateway zhdq-service-datamiddle-web zhdq-service-file zhdq-service-jobhandle zhdq-service-party-manage-web zhdq-service-party-meeting-web zhdq-service-party-member-web zhdq-service-xxl"               // 4. 需要检测存活的端口       CHECK_PORTS = "10200 10005 10206 13800 8012 10204 10203 28305"       // ==========================================   } ​   stages {       stage('拉取代码') {           steps {                echo "🚚 正在从 Gitea 拉取代码..."                git credentialsId: "${GITEA_CRED}",                   url: "ssh://git@${REMOTE_IP}:${GITEA_PORT}/liaoch_1/xh_backend.git",                   branch: 'main'           }       } ​       stage('离线编译') {           steps {                echo "🔨 开始 Maven 编译 (离线模式)..."               // -o 为离线模式,如果是第一次编译请去掉 -o                sh 'mvn clean package -DskipTests -o -s /var/jenkins_home/apache-maven-3.9.12/conf/settings.xml -Dmaven.repo.local=/var/jenkins_home/maven_repository'           }       } ​       stage('收集并远程推送') {           steps {                sh '''                #!/bin/bash                set -e                echo "📂 正在确保远程目录存在: ${REMOTE_DIR}"                ssh -p ${DEPLOY_PORT} ${REMOTE_USER}@${REMOTE_IP} "mkdir -p ${REMOTE_DIR}"                                echo "🔍 开始扫描并推送 JAR 包..."                for mod in $WHITELIST; do                    # 查找生成的 jar,排除 sources 和 javadoc                    jar_path=$(find . -path "*/target/${mod}*.jar" ! -name "*-sources.jar" ! -name "*-javadoc.jar" -type f | head -n 1)                                        if [ -n "$jar_path" ]; then                        echo "✅ 匹配成功: $mod -> 正在推送..."                        # 注意:scp 端口是大写 -P                       scp -P ${DEPLOY_PORT} "$jar_path" "${REMOTE_USER}@${REMOTE_IP}:${REMOTE_DIR}/${mod}.jar"                    else                        echo "⚠️ 未找到模块: $mod 的 JAR 文件"                    fi                done                '''           }       } ​       stage('远程重启并存活检测') {           steps {                sh '''                #!/bin/bash                ssh -p ${DEPLOY_PORT} ${REMOTE_USER}@${REMOTE_IP} "                    cd ${REMOTE_DIR}                                        echo '🚀 执行重启脚本...'                   ./app-manager-linux-amd64 restart ​                    echo '⏳ 进入 120 秒初始化等待期...'                    sleep 120 ​                    echo '🔍 开始进行端口健康检查...'                    FAILED_COUNT=0                    for port in ${CHECK_PORTS}; do                        # 同时尝试 ss 和 netstat 增加兼容性                        if ss -lnt | grep -q \\":\$port \\" || netstat -lnt | grep -q \\":\$port \\"; then                            echo \\"✅ [OK] 端口 \$port 正常启动\\"                        else                            echo \\"❌ [ERROR] 端口 \$port 未响应!\\"                            FAILED_COUNT=\$((FAILED_COUNT+1))                        fi                    done ​                    if [ \$FAILED_COUNT -gt 0 ]; then                        echo \\"------------------------------------------\\"                        echo \\"❌ 部署失败:共有 \$FAILED_COUNT 个模块未正常启动!\\"                        echo \\"------------------------------------------\\"                        exit 1                    else                        echo \\"------------------------------------------\\"                        echo \\"🎊 部署成功:所有微服务均已就绪!\\"                        echo \\"------------------------------------------\\"                    fi                "                '''           }       }   } ​   post {       always {            echo "🏁 流程结束。"       }       failure {            echo "❌ 构建失败,请检查 Jenkins 控制台输出和服务器日志。"       }   } } ​ ​

#前端Pipeline scripts ​ pipeline {   agent any ​   environment {       // 目标部署服务器信息 (137 宿主机)       REMOTE_IP = "192.168.253.137"       REMOTE_USER = "root"               // 生产环境目录       PROD_DIR = "/app/xhdj_dev/project_frontend"       // 备份/归档目录       UPDATE_DIR = "/app/xhdj_dev/project_update/frontend_${BUILD_NUMBER}"               // Jenkins 凭据 ID (请确保在 Jenkins 凭据里配置了私钥,且 ID 为此值)       GITEA_CRED = "gitea-ssh-key"   } ​   triggers {       // 只要 UI 界面配置了 token,脚本里加这段可以确保配置被固化       GenericTrigger(           genericVariables: [               [key: 'ref', value: '$.ref']           ],           token: 'xh_frontend_token',           causeString: 'Gitea Push: $ref',           printPostContent: true,           silentResponse: false       )   } ​   stages {       stage('Git Checkout') {           steps {                echo "🚚 正在从 Gitea 拉取前端代码..."               // 修正后的地址:使用 222 端口和 xh_frontend 仓库                git credentialsId: "${GITEA_CRED}",                   url: "ssh://[email protected]:222/liaoch_1/xh_frontend.git",                   branch: 'main'           }       } ​       stage('Front-end Build') {           steps {                echo "🛠️ 正在执行离线构建..."                sh """                # 1. 环境变量(必须加,否则找不到 node/yarn)                export NODE_HOME="/var/jenkins_home/node-v20.19.0"                export YARN_HOME="/var/jenkins_home/yarn-tool"                export PATH="\$NODE_HOME/bin:\$YARN_HOME/bin:\$PATH" ​                # 2. 注入依赖                CACHE_DIR="/var/jenkins_home/frontend_cache/node_modules"                if [ -d "\$CACHE_DIR" ]; then                    echo "📦 正在注入 node_modules..."                    # cp -rn 确保只拷贝缺少的文件,不重复覆盖                    cp -rn \$CACHE_DIR ./                else                    echo "❌ 找不到离线依赖目录" && exit 1                fi ​                # 3. 修复 tyarn 找不到的问题                # 将 package.json 里的 "tyarn" 替换为 "yarn"                if grep -q "tyarn" package.json; then                    sed -i 's/tyarn/yarn/g' package.json                fi ​                # 4. 执行构建               yarn build                """           }       } ​       stage('Archive & Deploy') {           steps {                echo "🚀 正在同步产物到 137 服务器 (精准模式)..."                sh """                # 1. 确保远程目录存在                ssh -o StrictHostKeyChecking=no ${REMOTE_USER}@${REMOTE_IP} "mkdir -p ${UPDATE_DIR} ${PROD_DIR}"                                # 2. 【核心修正】只传输内层真正的产物                # 注意这里的路径:dist/project_frontend/*                echo "📦 正在从 dist/project_frontend 提取文件..."               scp -r dist/project_frontend/* ${REMOTE_USER}@${REMOTE_IP}:${UPDATE_DIR}/                                # 3. 部署到生产目录                # 先清空,再用 /. 语法平铺拷贝                ssh ${REMOTE_USER}@${REMOTE_IP} "                    rm -rf ${PROD_DIR}/* && \                    cp -Rf ${UPDATE_DIR}/. ${PROD_DIR}/ && \                    echo '✅ 部署成功!目录内容:' && \                    ls -F ${PROD_DIR}                "                """           }       } ​       stage('Health Check') {           steps {                echo "🔍 检查 137 服务器上的部署结果..."                sh "ssh ${REMOTE_USER}@${REMOTE_IP} 'ls -l ${PROD_DIR}'"           }       }   } ​   post {       success {            echo "✅ 前端自动部署成功!"       }       failure {            echo "❌ 构建失败,请查看 Console Output。"       }   } }

3.gitea配置

#安装docker gitea tar xf docker-24.0.9.tgz cp docker/* /usr/bin/ vi /etc/systemd/system/docker.service systemctl daemon-reload systemctl enable docker systemctl start docker cp docker-compose-linux-x86_64 /usr/local/bin/docker-compose chmod +x /usr/local/bin/docker-compose docker-compose version cd /opt ​ [root@Kylin01 gitea]# vim docker-compose.yml version: "3" ​ networks: gitea:   external: false ​ services: server:   image: docker.gitea.com/gitea:1.21.4 # 建议使用稳定版,1.25目前可能过于超前   container_name: gitea   environment:      - USER_UID=1000      - USER_GID=1000      # 如果你以后想用 MySQL/PostgreSQL,可以在这里加环境变量    restart: always   networks:      - gitea   volumes:      - ./gitea:/data      - /etc/timezone:/etc/timezone:ro      - /etc/localtime:/etc/localtime:ro   ports:      - "3000:3000"  # Web 访问端口      - "222:22"     # SSH 端口 ​ id was chown -R was. /opt/gitea export DOCKER_API_VERSION=1.43 docker-compose up -d ​ vim gitea/conf/app.ini [webhook] ALLOWED_HOST_LIST = * SKIP_TLS_VERIFY = true DELIVER_TIMEOUT = 15s ​ docker restart gitea ​ #初始化项目目录 mkdir -p /app/xhdj_dev cd /app/xhdj_dev chmod +x app-manager-linux-amd64 ./app-manager-linux-amd64 init#gitea 代码仓库初始化 ​ 1.拉取需要迁移的前后端代码 先拉到本地电脑对应两个目录 git clone http://***.git 2.在git新建两个仓库 3.拿到仓库地址 http://192.168.253.137:3000/liaoch_1/xh_frontend.git http://192.168.253.137:3000/liaoch_1/xh_backend.git 4.将拉下来的代码推送上去 git push --mirror http://192.168.253.137:3000/liaoch_1/xh_frontend.git git push --mirror http://192.168.253.137:3000/liaoch_1/xh_backend.git ​ #webhook设置 前端:http://192.168.253.129:8082/generic-webhook-trigger/invoke?token=xh_frontend_token 后端:http://192.168.253.129:8082/generic-webhook-trigger/invoke?token=xh_backend_token(这里的tocken就是前面jenkins的tigger里定义的) ​ #ssh秘钥设置 就是前面在jenkins容器中获取到的pub公钥 root@49413caa42b7:/# cat /root/.ssh/id_rsa.pub

4.测试推送

#gitea测试推送 然后去jenkins下看是否有任务构建

#推送代码测试 1.本地电脑修改部分代码 2.目录终端执行 git add . git commit -m "测试 Webhook 自动触发构建" git push origin main 3.查看jenkins是否有新的任务构建 4.检验是否构建成功 5.查看对应项目目录下是否有构建成功的jar包 前端文件 后端应用是否成功启动

Read more

github学生认证(Github Copilot)

github学生认证(Github Copilot)

今天想配置一下Github Copilot,认证学生可以免费使用一年,认证过程中因为各种原因折腾了好久,记录一下解决方法供大家参考。 p.s.本文章只针对Github学生认证部分遇到的问题及解决方法,不包括配置copilot的全部流程~ 1、准备工作 在认证学生身份之前,首先需要有一个github的账户。进入个人信息编辑页面,确保email邮箱有edu结尾的邮箱,如果账户一开始不是用edu邮箱注册的话,可以点Add email address添加你的教育邮箱,然后完成邮箱验证。 2、个人信息填写 验证完教育邮箱之后,要补充个人信息。有以下几项要填。 Name填写个人的真实英文名,比如张三就填Zhang San;Bio用英文填写学校和专业名称;URL填学校官网网址。 Company填学校名称;Location填学校地址;Display current local time可以勾上。全部填好之后点Update profile保存。 3、更新Billing & plans / Payment information 这一步挺重要的,要注意这里的billing info

By Ne0inhk
2026 最新版|学生认证白嫖 GitHub Copilot Pro 保姆级教程

2026 最新版|学生认证白嫖 GitHub Copilot Pro 保姆级教程

2026 最新版|学生认证白嫖 GitHub Copilot Pro 保姆级教程 作为编程党,谁能拒绝免费的 Copilot Pro?每月省 10 $,解锁无限制代码补全、Anthropic Claude Sonnet 4, GPT-5, Gemini 2.5 Pro等高级模型、每月 300 次 Premium 请求,学生身份认证就能直接白嫖,全程零成本,亲测 2026 年有效!这篇教程把所有步骤、避坑点都捋清楚了,跟着做一遍过,再也不用受免费版额度的气! 前言 先说说为什么一定要冲 Copilot Pro:免费版每月只有 2000 次代码补全 + 50 次聊天请求,写代码刚进入状态就提示额度用完,体验感拉胯;而 Pro

By Ne0inhk
GitHub Copilot 学生认证详细教程

GitHub Copilot 学生认证详细教程

GitHub Copilot 是 GitHub 提供的 AI 代码助手工具,学生可以通过 GitHub Student Developer Pack(学生开发者包)免费获取 Copilot Pro 版本(通常每月收费 10 美元)。这个过程涉及验证你的学生身份,一旦通过,你可以免费使用 Copilot Pro,直到你的学生身份到期(通常每年需要重新验证)。以下是最详细的教程,基于 GitHub 官方文档和社区指南,涵盖从准备到激活的所有步骤。我会逐步分解,确保每个步骤都清晰、可操作。如果你是第一次申请,预计整个过程可能需要 1-3 天(验证通常在 72 小时内完成)。 第一部分:资格要求和准备工作 在开始前,确保你符合条件。如果不符合,申请会被拒绝。 * 资格标准: * 你必须是当前在读学生,

By Ne0inhk

Claude Code的完美平替:OpenCode + GitHub Copilot

引言:Claude 虽好,但你真的能用上吗? 在当前席卷全球的“Vibe Coding”浪潮中,Anthropic 推出的 Claude 系列模型 + 终端工具 Claude Code,凭借极强的逻辑推理能力,成为了开发者眼中的“白月光”。但现实是残酷的:对于中国开发者而言,账号随时被封、海外信用卡支付遭拒、API 额度受限以及复杂的网络环境,构成了一道难以逾越的门槛。 虽然最近国产编程模型不断发力,Claude Code + GLM-4.7的表现非常出色,但面对复杂问题,Claude系列模型依然完胜。难道我们只能眼馋Claude全家桶的编程体验吗? 作为一名追求极致生产力的开发者,我发现了一个绝佳的完美替代方案:OpenCode + GitHub Copilot。这个组合不仅能让你享受如 GLM-4.7 一样的性价比,还能更方便的使用 Claude 的顶级模型。 Claude Code 的开源免费平替:OpenCode 想要复刻

By Ne0inhk