Compare commits
10 Commits
d4275c767d
...
edfb8aedba
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
edfb8aedba | ||
|
|
37d7e06d38 | ||
|
|
501f70d489 | ||
|
|
ba3727723e | ||
|
|
79742d25f8 | ||
|
|
5b34880b11 | ||
|
|
b3d8750816 | ||
|
|
cb75e7096a | ||
|
|
095346b48e | ||
|
|
29bcbbdb72 |
@ -1,5 +1,6 @@
|
||||
# 多阶段构建:第一阶段用 Node 构建产物,第二阶段用 Nginx 托管静态资源
|
||||
# 用 Node 22 LTS:Node 20.19.x 上 corepack/pnpm 现代版有 VM builtin module 兼容问题
|
||||
# base 镜像走 Harbor Proxy Cache(dockerhub-proxy → docker.io),避免公网拉取超时
|
||||
FROM node:22-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
@ -8,6 +9,13 @@ WORKDIR /app
|
||||
# corepack 在 alpine 上即便 prepare 也不可靠 shim 出 pnpm 命令,npm 装最稳妥
|
||||
RUN npm install -g pnpm@11.9.0
|
||||
|
||||
# npm 包源走阿里 npmmirror(国内最稳,避免 registry.npmjs.org 拉取不稳)
|
||||
# 调大 fetch 超时与重试,防止 install 中断
|
||||
RUN pnpm config set registry https://registry.npmmirror.com \
|
||||
&& pnpm config set fetch-timeout 600000 \
|
||||
&& pnpm config set fetch-retries 5 \
|
||||
&& pnpm config set fetch-retry-mintimeout 10000
|
||||
|
||||
# 先拷贝依赖清单,利用 Docker 层缓存
|
||||
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
|
||||
|
||||
|
||||
124
Jenkinsfile
vendored
124
Jenkinsfile
vendored
@ -1,101 +1,31 @@
|
||||
// BuildFlow buildflow-portal CI/CD - clean version
|
||||
// ①拉取代码 -> ②编译打包 -> 推送Harbor -> ③部署 + 飞书通知
|
||||
|
||||
def feishuNotify = { String result, String failedStageName = "" ->
|
||||
def dur = currentBuild.durationString.replace(" and counting", "")
|
||||
def commit = ""
|
||||
try { commit = sh(script: "git log -1 --pretty=format:%s", returnStdout: true).trim() } catch(ignored) {}
|
||||
def isOk = (result == "SUCCESS")
|
||||
def color = isOk ? "green" : "red"
|
||||
def statusText = isOk ? "成功" : "失败"
|
||||
def title = "CI/CD " + statusText + " " + env.JOB_NAME + " #" + env.BUILD_NUMBER
|
||||
def fields = [
|
||||
[is_short: true, text: [tag: "lark_md", content: "**项目**\n" + env.JOB_NAME]],
|
||||
[is_short: true, text: [tag: "lark_md", content: "**耗时**\n" + dur]],
|
||||
[is_short: true, text: [tag: "lark_md", content: "**提交**\n" + commit.take(50)]],
|
||||
[is_short: true, text: [tag: "lark_md", content: "**状态**\n" + statusText]]
|
||||
]
|
||||
def elements = [[tag: "div", fields: fields], [tag: "hr"]]
|
||||
if (isOk) {
|
||||
elements.add([tag: "div", text: [tag: "lark_md", content: "镜像地址\n`" + env.IMAGE + ":" + env.TAG + "`"]])
|
||||
} else if (failedStageName) {
|
||||
elements.add([tag: "div", text: [tag: "lark_md", content: "失败阶段\n" + failedStageName]])
|
||||
}
|
||||
def burl = env.BUILD_URL
|
||||
if (burl == null) { burl = "http://115.191.51.246:8080/job/" + env.JOB_NAME.replace("/", "/job/") + "/" }
|
||||
elements.add([tag: "action", actions: [[tag: "button", text: [tag: "plain_text", content: "查看构建"], url: burl, type: isOk ? "primary" : "danger"]]])
|
||||
def card = [msg_type: "interactive", card: [header: [title: [tag: "plain_text", content: title], template: color], elements: elements]]
|
||||
def jsonStr = groovy.json.JsonOutput.toJson(card)
|
||||
writeFile file: "/tmp/feishu_msg.json", text: jsonStr
|
||||
sh 'curl -sS -X POST https://open.feishu.cn/open-apis/bot/v2/hook/d215e8ed-edea-4f07-a77c-2a0e5b52d9b9 -H "Content-Type: application/json" -d @/tmp/feishu_msg.json || true'
|
||||
sh 'rm -f /tmp/feishu_msg.json'
|
||||
}
|
||||
|
||||
pipeline {
|
||||
agent any
|
||||
environment {
|
||||
HARBOR = "43.134.235.58:58080"
|
||||
IMAGE = "${HARBOR}/buildflow/buildflow-portal"
|
||||
TAG = "${env.BUILD_NUMBER}"
|
||||
agent any
|
||||
options { timestamps() }
|
||||
environment {
|
||||
IMG = "127.0.0.1/middleware/buildflow-portal"
|
||||
}
|
||||
stages {
|
||||
stage("Build image") {
|
||||
steps {
|
||||
sh "docker build -t ${IMG}:latest -t ${IMG}:${BUILD_NUMBER} ."
|
||||
}
|
||||
}
|
||||
options {
|
||||
timeout(time: 30, unit: "MINUTES")
|
||||
buildDiscarder(logRotator(numToKeepStr: "10"))
|
||||
stage("Push to Harbor") {
|
||||
steps {
|
||||
withCredentials([usernamePassword(credentialsId: "harbor-admin", usernameVariable: "HU", passwordVariable: "HP")]) {
|
||||
sh 'echo "$HP" | docker login 127.0.0.1 -u "$HU" --password-stdin'
|
||||
sh "docker push ${IMG}:latest && docker push ${IMG}:${BUILD_NUMBER}"
|
||||
}
|
||||
}
|
||||
}
|
||||
stages {
|
||||
stage("① 拉取代码") {
|
||||
steps {
|
||||
checkout scm
|
||||
sh "git log --oneline -3"
|
||||
}
|
||||
}
|
||||
stage("② 编译打包") {
|
||||
steps {
|
||||
sh "docker build -t ${IMAGE}:${TAG} -t ${IMAGE}:latest ."
|
||||
sh "docker images | grep ${IMAGE} | head -2"
|
||||
}
|
||||
}
|
||||
stage("推送 Harbor") {
|
||||
steps {
|
||||
withCredentials([usernamePassword(credentialsId: "harbor-credentials", usernameVariable: "HUSER", passwordVariable: "HPASS")]) {
|
||||
sh '''
|
||||
echo $HPASS | docker login $HARBOR -u $HUSER --password-stdin
|
||||
docker push $IMAGE:$TAG
|
||||
docker push $IMAGE:latest
|
||||
docker logout $HARBOR
|
||||
'''
|
||||
}
|
||||
}
|
||||
}
|
||||
stage("③ 部署") {
|
||||
steps {
|
||||
script {
|
||||
try {
|
||||
node("deploy-agent") {
|
||||
sh '''
|
||||
docker pull $IMAGE:latest
|
||||
docker rm -f buildflow-portal 2>/dev/null || true
|
||||
docker run -d --name buildflow-portal --restart unless-stopped -p 5174:5173 $IMAGE:latest
|
||||
'''
|
||||
}
|
||||
echo "部署完成"
|
||||
} catch (e) {
|
||||
echo "部署节点暂不可用,跳过自动部署。镜像已推送 Harbor。"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage("Deploy") {
|
||||
steps {
|
||||
sh "docker compose -f /home/xiangning/deploy/buildflow/docker-compose.yml pull buildflow-portal"
|
||||
sh "docker compose -f /home/xiangning/deploy/buildflow/docker-compose.yml up -d --no-deps buildflow-portal"
|
||||
}
|
||||
}
|
||||
post {
|
||||
success {
|
||||
script { feishuNotify("SUCCESS") }
|
||||
}
|
||||
failure {
|
||||
script { feishuNotify("FAILURE", "构建失败,请查看日志") }
|
||||
}
|
||||
always {
|
||||
sh "docker rmi ${IMAGE}:${TAG} ${IMAGE}:latest 2>/dev/null || true"
|
||||
deleteDir()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
always { sh "docker image prune -f || true" }
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,6 +21,18 @@ services:
|
||||
networks:
|
||||
- buildflow-net
|
||||
|
||||
# KKFileView 在线文档预览服务(Office/PDF/图片等 30+ 格式)
|
||||
# 后端 file 模块的 /api/files/office-preview 端点生成预览地址指向此服务。
|
||||
# 本地预览:http://localhost:8012
|
||||
kkfileview:
|
||||
image: keking/kkfileview:4.4.0
|
||||
container_name: buildflow-kkfileview
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8012:8012"
|
||||
networks:
|
||||
- buildflow-net
|
||||
|
||||
networks:
|
||||
buildflow-net:
|
||||
driver: bridge
|
||||
|
||||
14
nginx.conf
14
nginx.conf
@ -35,6 +35,7 @@ server {
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
|
||||
|
||||
# 静态资源长缓存(Vite 产物带 hash 文件名,可安全 immutable)
|
||||
location /assets/ {
|
||||
@ -61,14 +62,25 @@ server {
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
# SPA history 路由回退
|
||||
# SPA history 路由回退(加安全头,nginx add_header 继承规则:location 有自己的 add_header 时不继承 server)
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
|
||||
}
|
||||
|
||||
# index.html 不缓存,保证发布后立即生效
|
||||
location = /index.html {
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
# 安全响应头(同理重复,防继承覆盖)
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
|
||||
expires 0;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user