init: demo-frontend with CI/CD template
Some checks failed
Build & Deploy Frontend / build (push) Has been cancelled
Build & Deploy Frontend / deploy (push) Has been cancelled

This commit is contained in:
joe
2026-07-19 02:13:20 +08:00
committed by Joe
parent ed58df3cfb
commit fe1c93eacc
6 changed files with 242 additions and 0 deletions

View File

@@ -0,0 +1,71 @@
# Gitea Actions workflow for Node/Vue/React frontend
# Place at: <repo>/.gitea/workflows/build.yml
name: Build & Deploy Frontend
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
REGISTRY: 127.0.0.1:5000
IMAGE_NAME: web-frontend
jobs:
build:
runs-on: k3s-runner-01
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci || npm install
- name: Build
run: npm run build
env:
# 注入构建时环境变量
VITE_API_URL: ${{ vars.API_URL }}
- name: Build & push Docker image
env:
REGISTRY: ${{ env.REGISTRY }}
run: |
IMAGE_TAG="${{ env.REGISTRY }}/${IMAGE_NAME}:${GITHUB_SHA::8}"
docker build \
--build-arg VITE_API_URL="${{ vars.API_URL }}" \
-t "${IMAGE_TAG}" \
-t "${REGISTRY}/${IMAGE_NAME}:latest" \
-f Dockerfile .
docker push "${IMAGE_TAG}"
docker push "${REGISTRY}/${IMAGE_NAME}:latest"
echo "IMAGE_TAG=${IMAGE_TAG}" >> $GITHUB_OUTPUT
echo "DEPLOY_IMAGE=${REGISTRY}/${IMAGE_NAME}:${GITHUB_SHA::8}" >> $GITHUB_ENV
- name: Update ArgoCD app
run: |
# 触发 ArgoCD 重新同步
curl -sk -X POST \
-H "Authorization: Bearer ${{ secrets.ARGOCD_TOKEN }}" \
"https://argocd.k3s-runner.cicd.svc.cluster.local/api/v1/applications/web-frontend/restart"
deploy:
needs: build
runs-on: k3s-runner-01
if: github.ref == 'refs/heads/main'
steps:
- name: Trigger ArgoCD sync
env:
IMAGE: ${{ needs.build.outputs.IMAGE_TAG }}
run: |
# Patch ArgoCD Application 指向新镜像
kubectl -n argocd patch application web-frontend --type merge \
-p "{\"spec\":{\"source\":{\"helm\":{\"parameters\":[{\"name\":\"image.tag\",\"value\":\"${GITHUB_SHA::8}\"}]}}}}"

16
Dockerfile Normal file
View File

@@ -0,0 +1,16 @@
# Frontend Dockerfile (nginx + 静态产物)
# 多阶段构建
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci || npm install
COPY . .
RUN npm run build
FROM nginx:1.27-alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
HEALTHCHECK --interval=30s --timeout=3s CMD wget -q -O- http://localhost/ || exit 1
CMD ["nginx", "-g", "daemon off;"]

9
deploy/helm/Chart.yaml Normal file
View File

@@ -0,0 +1,9 @@
# Minimal Helm chart for any service
# Place at: <repo>/deploy/helm/
apiVersion: v2
name: demo-frontend
description: Standard Helm chart for k3s services
type: application
version: 0.1.0
appVersion: "1.0.0"

View File

@@ -0,0 +1,85 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Chart.Name }}
labels:
app: {{ .Chart.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ .Chart.Name }}
template:
metadata:
labels:
app: {{ .Chart.Name }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.registry }}/{{ .Values.image.name }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.service.port }}
protocol: TCP
{{- if .Values.probes.liveness.enabled }}
livenessProbe:
httpGet:
path: {{ .Values.probes.liveness.path }}
port: {{ .Values.probes.liveness.port }}
initialDelaySeconds: 30
periodSeconds: 10
{{- end }}
{{- if .Values.probes.readiness.enabled }}
readinessProbe:
httpGet:
path: {{ .Values.probes.readiness.path }}
port: {{ .Values.probes.readiness.port }}
initialDelaySeconds: 5
periodSeconds: 5
{{- end }}
env:
{{- toYaml .Values.env | nindent 12 }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Chart.Name }}
spec:
type: {{ .Values.service.type }}
selector:
app: {{ .Chart.Name }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
{{- if .Values.ingress.enabled }}
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ .Chart.Name }}
annotations:
{{- if .Values.ingress.className }}
kubernetes.io/ingress.class: {{ .Values.ingress.className }}
{{- end }}
spec:
{{- if .Values.ingress.className }}
ingressClassName: {{ .Values.ingress.className }}
{{- end }}
rules:
- host: {{ .Values.ingress.host }}
http:
paths:
- path: {{ .Values.ingress.path | default "/" }}
pathType: Prefix
backend:
service:
name: {{ .Chart.Name }}
port:
number: {{ .Values.service.port }}
{{- end }}

38
deploy/helm/values.yaml Normal file
View File

@@ -0,0 +1,38 @@
# Default values
image:
registry: 127.0.0.1:5000
name: demo-frontend
tag: latest
pullPolicy: Always
replicaCount: 2
service:
type: ClusterIP
port: 8080
ingress:
enabled: false
className: traefik
host: demo-frontend.local
path: /
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
env: []
probes:
liveness:
enabled: true
path: /healthz
port: 8080
readiness:
enabled: true
path: /healthz
port: 8080

23
nginx.conf Normal file
View File

@@ -0,0 +1,23 @@
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
# SPA fallback
location / {
try_files $uri $uri/ /index.html;
}
# 静态资源缓存
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# 健康检查
location /healthz {
access_log off;
return 200 "ok\n";
}
}