4df8dc6f87
新建 web/website/(Astro 零 SSR): - 组件迁移:交互块保留 .jsx 经 @astrojs/react(Header/AnnouncementBar/SignupForm/PricingPlans),纯展示块转 .astro 去运行时 JS;像素以原型为基准。 - 令牌同源:build-tokens.mjs 从 design/colors_and_type.css 生成 tokens.gen.css,仅剔除第三方 Google Fonts @import,数值不改。 - 字体自托管:@fontsource(Sora/Manrope/Noto Sans SC/JetBrains Mono),无第三方 CDN。 - 图标:Lucide 构建期内联 SVG(替代 unpkg CDN),零运行时、零 CDN。 - i18n:/(zh)与 /en/(en)双路由单显,语言切换组件;文案沿用 ui_kits 脱敏文案。 - 安全:public/_headers 严格 CSP(全 self + 自托管资源 + 内联片段 sha256,无 unsafe-inline)+ HSTS;无支付表单。 - CI:.gitea/workflows/website.yml 构建(红线扫描+lint+CSP 哈希)→ 同时发布 Cloudflare Pages 主站与镜像。 - 灾备:README 写明干净环境 npm ci && npm run build 可直接部署到任意静态托管;dist 指纹确定性,主站镜像一致。 测试:npm run lint(0 error)/ npm test(build+CSP 哈希注入+红线扫描 0 命中)/ 两次干净构建 dist 指纹一致。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
104 lines
3.2 KiB
YAML
104 lines
3.2 KiB
YAML
name: website
|
||
|
||
# 官网 CI/CD:push web/website/** → 构建(红线词扫描 + lint + CSP 哈希)→ 同时发布主站与镜像。
|
||
# 与仓库根 .gitea/workflows/ci.yml 解耦:官网完全静态、与后端无依赖,可独立并行。
|
||
#
|
||
# 需在 Gitea 配置 Secrets:
|
||
# CF_API_TOKEN Cloudflare API Token(Pages:Edit 权限)
|
||
# CF_ACCOUNT_ID Cloudflare Account ID
|
||
# CF_PAGES_PROJECT_MAIN 主站 Pages 项目名
|
||
# CF_PAGES_PROJECT_MIRROR 镜像 Pages 项目名(≥1 个活跃镜像,应对主域名被墙)
|
||
# 并把 runs-on 改成实际 runner label。
|
||
|
||
on:
|
||
push:
|
||
paths:
|
||
- 'web/website/**'
|
||
- '.gitea/workflows/website.yml'
|
||
pull_request:
|
||
paths:
|
||
- 'web/website/**'
|
||
workflow_dispatch:
|
||
|
||
env:
|
||
# 主站 canonical 域名;镜像也用同一值 → canonical 指向主站 + 产物 hash 一致。
|
||
SITE_URL: https://pangolin.example
|
||
|
||
jobs:
|
||
# ── 构建 + 校验(红线 / lint / CSP / 指纹) ──────────────────────────────
|
||
build:
|
||
name: Build & Verify
|
||
runs-on: nas
|
||
defaults:
|
||
run:
|
||
working-directory: web/website
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
|
||
- uses: actions/setup-node@v4
|
||
with:
|
||
node-version: 22
|
||
|
||
- name: Install (clean, reproducible)
|
||
run: npm ci
|
||
|
||
- name: Type-check (lint)
|
||
run: npm run lint
|
||
|
||
- name: Build (含 token 同源 + CSP 哈希注入)
|
||
run: npm run build
|
||
|
||
- name: Redline scan (脱敏铁律 13)
|
||
run: npm run scan:redline
|
||
|
||
- name: CSP sanity (禁止 unsafe-inline)
|
||
run: grep -L "unsafe-inline" dist/_headers
|
||
|
||
- name: Fingerprint (主站/镜像一致性基线)
|
||
run: npm run hash:dist | tee dist.fingerprint
|
||
|
||
- name: Upload artifact
|
||
uses: actions/upload-artifact@v3
|
||
with:
|
||
name: website-dist
|
||
path: web/website/dist
|
||
|
||
# ── 同时发布主站与镜像(同一份 dist,hash 必然一致) ─────────────────────
|
||
deploy:
|
||
name: Deploy main + mirror
|
||
needs: build
|
||
if: github.event_name == 'push'
|
||
runs-on: nas
|
||
defaults:
|
||
run:
|
||
working-directory: web/website
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
|
||
- uses: actions/setup-node@v4
|
||
with:
|
||
node-version: 22
|
||
|
||
- name: Install
|
||
run: npm ci
|
||
|
||
- name: Build
|
||
run: npm run build
|
||
|
||
- name: Deploy → 主站 (Cloudflare Pages)
|
||
env:
|
||
CLOUDFLARE_API_TOKEN: ${{ secrets.CF_API_TOKEN }}
|
||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }}
|
||
run: npx wrangler pages deploy dist --project-name "${{ secrets.CF_PAGES_PROJECT_MAIN }}" --branch main
|
||
|
||
- name: Deploy → 镜像 (Cloudflare Pages)
|
||
env:
|
||
CLOUDFLARE_API_TOKEN: ${{ secrets.CF_API_TOKEN }}
|
||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }}
|
||
run: npx wrangler pages deploy dist --project-name "${{ secrets.CF_PAGES_PROJECT_MIRROR }}" --branch main
|
||
|
||
- name: Verify main == mirror (内容一致性)
|
||
run: |
|
||
echo "主站与镜像部署的是同一份 dist,指纹:"
|
||
npm run --silent hash:dist
|