# 对齐 project0(projectBase)CI:Gitea + Docker + K8s # 本仓为 Vite 静态宿主,构建产物为 dist/,镜像用 nginx 监听 3000 name: CI Pipeline on: push: branches: [master] workflow_dispatch: jobs: check-commit: runs-on: ubuntu-latest outputs: should_build: ${{ steps.check.outputs.should_build }} should_deploy: ${{ steps.check.outputs.should_deploy }} should_deploy_only: ${{ steps.check.outputs.should_deploy_only }} steps: - name: Checkout Code uses: actions/checkout@v4 with: lfs: false fetch-depth: 1 - name: Check commit message id: check run: | if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then echo "should_build=true" >> $GITHUB_OUTPUT echo "should_deploy=true" >> $GITHUB_OUTPUT echo "should_deploy_only=false" >> $GITHUB_OUTPUT echo "✅ 手动触发" else head_msg="${{ github.event.head_commit.message }}" echo "检查提交消息: $head_msg" if echo "$head_msg" | grep -qE '\[deploy-only\]'; then echo "should_build=false" >> $GITHUB_OUTPUT echo "should_deploy=true" >> $GITHUB_OUTPUT echo "should_deploy_only=true" >> $GITHUB_OUTPUT echo "✅ 找到 [deploy-only] 标记,只执行部署(跳过构建)" elif echo "$head_msg" | grep -qE '\[build\]|\[deploy\]'; then echo "should_build=true" >> $GITHUB_OUTPUT echo "should_deploy_only=false" >> $GITHUB_OUTPUT echo "✅ 找到 [build] 或 [deploy] 标记" if echo "$head_msg" | grep -qE '\[deploy\]'; then echo "should_deploy=true" >> $GITHUB_OUTPUT echo "✅ 找到 [deploy] 标记,将执行构建和部署" else echo "should_deploy=false" >> $GITHUB_OUTPUT echo "✅ 只找到 [build] 标记,只执行构建" fi else echo "should_build=false" >> $GITHUB_OUTPUT echo "should_deploy=false" >> $GITHUB_OUTPUT echo "should_deploy_only=false" >> $GITHUB_OUTPUT echo "ℹ️ 提交消息不包含 [build]、[deploy] 或 [deploy-only] 标记,跳过工作流" fi fi build: needs: check-commit if: needs.check-commit.outputs.should_build == 'true' runs-on: ubuntu-latest outputs: image_ref: ${{ steps.push.outputs.image_ref }} steps: - name: Checkout Code uses: actions/checkout@v4 with: lfs: false persist-credentials: true token: ${{ secrets.LFS_PAT || secrets.GITHUB_TOKEN }} - name: Force LFS Pull with Custom Auth run: | HOST="git.bigmonk.org" REPO_PATH="${{ github.repository }}" if [ -z "${{ secrets.LFS_PAT }}" ]; then echo "❌ 错误: 未配置 LFS_PAT Secret" exit 1 fi GIT_TOKEN="${{ secrets.LFS_PAT }}" git config --local --unset credential.helper || true git config --global --unset credential.helper || true AUTH_REMOTE="https://x-access-token:${GIT_TOKEN}@${HOST}/${REPO_PATH}.git" AUTH_LFS_URL="https://x-access-token:${GIT_TOKEN}@${HOST}/${REPO_PATH}.git/info/lfs" git remote set-url origin "${AUTH_REMOTE}" git config --local lfs.url "${AUTH_LFS_URL}" git lfs install git lfs pull echo "✅ LFS files pulled successfully" - name: Setup Node.js and pnpm uses: actions/setup-node@v4 with: node-version: '24' - name: Install pnpm run: | corepack enable corepack prepare pnpm@12.4.2 --activate - name: Configure npm cache proxy and retries run: | echo "NPM_CONFIG_REGISTRY=http://172.17.0.1:4873" >> $GITHUB_ENV echo "npm_config_registry=http://172.17.0.1:4873" >> $GITHUB_ENV echo "PNPM_CONFIG_REGISTRY=http://172.17.0.1:4873" >> $GITHUB_ENV pnpm config set registry http://172.17.0.1:4873 pnpm config set fetch-retries 6 pnpm config set fetch-retry-factor 2 pnpm config set fetch-retry-mintimeout 20000 pnpm config set fetch-retry-maxtimeout 120000 pnpm config set network-concurrency 8 pnpm config set child-concurrency 4 - name: Install dependencies run: | pnpm install --frozen-lockfile --prefer-offline \ || pnpm install --frozen-lockfile --prefer-offline --registry=https://registry.npmjs.org - name: Build Vite application run: | pnpm build echo "✅ Build completed" if [ -d "dist" ] && [ -f "dist/index.html" ]; then echo "✅ dist/index.html 存在" else echo "❌ dist 构建产物不存在" exit 1 fi - name: Verify Docker access run: docker info - name: Build Docker image run: | export REGISTRY="104.225.236.253:30500" export REPO_NAME="${{ github.event.repository.name }}" export IMAGE_LATEST="${REGISTRY}/${REPO_NAME}:latest" export IMAGE_SHA="${REGISTRY}/${REPO_NAME}:${GITHUB_SHA}" docker build -t "${IMAGE_LATEST}" -t "${IMAGE_SHA}" . echo "Image built: ${IMAGE_LATEST}" echo "Image built: ${IMAGE_SHA}" - name: Push to registry (SHA + latest, deploy by digest) id: push run: | set -euo pipefail export REGISTRY="104.225.236.253:30500" export REPO_NAME="${{ github.event.repository.name }}" export IMAGE_LATEST="${REGISTRY}/${REPO_NAME}:latest" export IMAGE_SHA="${REGISTRY}/${REPO_NAME}:${GITHUB_SHA}" docker push "${IMAGE_SHA}" docker push "${IMAGE_LATEST}" IMAGE_REF="${IMAGE_SHA}" DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' "${IMAGE_SHA}" 2>/dev/null || true) if [ -n "${DIGEST}" ]; then IMAGE_REF="${DIGEST}" fi echo "image_ref=${IMAGE_REF}" >> "${GITHUB_OUTPUT}" echo "✅ Pushed ${IMAGE_SHA} and ${IMAGE_LATEST}" echo "✅ Rollout ref (digest preferred): ${IMAGE_REF}" deploy: needs: [check-commit, build] if: needs.check-commit.outputs.should_deploy == 'true' && needs.check-commit.outputs.should_deploy_only != 'true' runs-on: ubuntu-latest steps: - name: Deploy to K8s run: | if [ -z "${{ secrets.KUBECONFIG_BASE64 }}" ]; then echo "INFO: KUBECONFIG_BASE64 not configured. Deployment skipped." exit 0 fi mkdir -p ~/.kube echo "${{ secrets.KUBECONFIG_BASE64 }}" | base64 -d > ~/.kube/config chmod 600 ~/.kube/config kubectl cluster-info || { echo "ERROR: Failed to connect to K8s cluster"; exit 1; } export REPO_NAME="${{ github.event.repository.name }}" export IMAGE_REF="${{ needs.build.outputs.image_ref }}" export NAMESPACE="ns-${REPO_NAME}" export DEPLOYMENT="${REPO_NAME}-deploy" echo "Deploying ${IMAGE_REF} to ${NAMESPACE}/${DEPLOYMENT}" kubectl patch "deployment/${DEPLOYMENT}" -n "${NAMESPACE}" --type='json' -p='[ {"op":"replace","path":"/spec/strategy/rollingUpdate/maxSurge","value":"100%"}, {"op":"replace","path":"/spec/strategy/rollingUpdate/maxUnavailable","value":0}, {"op":"replace","path":"/spec/template/spec/containers/0/image","value":"'"${IMAGE_REF}"'"}, {"op":"replace","path":"/spec/template/spec/containers/0/imagePullPolicy","value":"IfNotPresent"}, {"op":"replace","path":"/spec/template/spec/containers/0/readinessProbe/httpGet/path","value":"/api/health"}, {"op":"replace","path":"/spec/template/spec/containers/0/readinessProbe/timeoutSeconds","value":5}, {"op":"replace","path":"/spec/template/spec/containers/0/livenessProbe/httpGet/path","value":"/api/health"}, {"op":"replace","path":"/spec/template/spec/containers/0/livenessProbe/timeoutSeconds","value":5} ]' kubectl rollout status "deployment/${DEPLOYMENT}" -n "${NAMESPACE}" --timeout=180s echo "✅ Deployment completed successfully" deploy-only: needs: check-commit if: needs.check-commit.outputs.should_deploy_only == 'true' runs-on: ubuntu-latest steps: - name: Deploy to K8s (without build) run: | if [ -z "${{ secrets.KUBECONFIG_BASE64 }}" ]; then echo "INFO: KUBECONFIG_BASE64 not configured. Deployment skipped." exit 0 fi mkdir -p ~/.kube echo "${{ secrets.KUBECONFIG_BASE64 }}" | base64 -d > ~/.kube/config chmod 600 ~/.kube/config kubectl cluster-info || { echo "ERROR: Failed to connect to K8s cluster"; exit 1; } export REGISTRY="104.225.236.253:30500" export REPO_NAME="${{ github.event.repository.name }}" export IMAGE_LATEST="${REGISTRY}/${REPO_NAME}:latest" export NAMESPACE="ns-${REPO_NAME}" export DEPLOYMENT="${REPO_NAME}-deploy" echo "Deploying existing image ${IMAGE_LATEST} to ${NAMESPACE}/${DEPLOYMENT}" kubectl patch "deployment/${DEPLOYMENT}" -n "${NAMESPACE}" --type='json' -p='[ {"op":"replace","path":"/spec/strategy/rollingUpdate/maxSurge","value":"100%"}, {"op":"replace","path":"/spec/strategy/rollingUpdate/maxUnavailable","value":0}, {"op":"replace","path":"/spec/template/spec/containers/0/image","value":"'"${IMAGE_LATEST}"'"}, {"op":"replace","path":"/spec/template/spec/containers/0/imagePullPolicy","value":"Always"}, {"op":"replace","path":"/spec/template/spec/containers/0/readinessProbe/httpGet/path","value":"/api/health"}, {"op":"replace","path":"/spec/template/spec/containers/0/readinessProbe/timeoutSeconds","value":5}, {"op":"replace","path":"/spec/template/spec/containers/0/livenessProbe/httpGet/path","value":"/api/health"}, {"op":"replace","path":"/spec/template/spec/containers/0/livenessProbe/timeoutSeconds","value":5} ]' kubectl rollout status "deployment/${DEPLOYMENT}" -n "${NAMESPACE}" --timeout=180s echo "✅ Deployment completed (using existing image)"