feat(demo): deploy published releases to Vercel (#15)

This commit is contained in:
RuoJi6 authored and GitHub committed 2026-10-15 15:47:03 +08:00
1 parent e94a7d472b
commit db371be93c
5 files changed
+248 -1

No files matched your search

+189
View File
@@ -0,0 +1,189 @@
name: deploy-demo
on:
# 网页手工发布的正式版;Actions 自己发布的版本由 workflow_call 接入。
release:
types: [published]
workflow_call:
inputs:
tag:
required: true
type: string
commit:
required: true
type: string
secrets:
VERCEL_TOKEN:
required: true
workflow_dispatch:
inputs:
tag:
description: "已发布的最新正式版标签;留空自动选择最新正式版"
required: false
type: string
permissions:
contents: read
# 不中断正在上传的部署;旧版本在构建前和上线前都会检查是否仍为 latest。
concurrency:
group: demo-production
cancel-in-progress: false
jobs:
deploy:
if: github.event_name != 'release' || github.event.release.prerelease == false
runs-on: ubuntu-latest
timeout-minutes: 25
env:
VERCEL_ORG_ID: ${{ vars.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ vars.VERCEL_PROJECT_ID }}
DEMO_URL: ${{ vars.DEMO_URL }}
NEXT_PUBLIC_MOCK: "1"
NEXT_TELEMETRY_DISABLED: "1"
steps:
- name: Select a published, complete, latest release
id: release
uses: actions/github-script@v7
env:
REQUESTED_TAG: ${{ inputs.tag || github.event.release.tag_name }}
with:
script: |
const { owner, repo } = context.repo;
const { data: latest } = await github.rest.repos.getLatestRelease({ owner, repo });
const tag = process.env.REQUESTED_TAG || latest.tag_name;
if (!/^v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/.test(tag)) {
throw new Error('只允许 v主版本.次版本.修订号 的正式标签');
}
const { data: release } = await github.rest.repos.getReleaseByTag({ owner, repo, tag });
if (release.draft || release.prerelease) throw new Error('草稿和预发布版本不可部署');
const version = tag.slice(1);
const assets = new Set(release.assets.map(asset => asset.name));
for (const platform of ['linux-amd64', 'linux-arm64', 'darwin-amd64', 'darwin-arm64', 'windows-amd64']) {
if (!assets.has(`artex-${version}-${platform}.zip`)) throw new Error(`缺少 ${platform} 发布包`);
}
if (!assets.has('SHA256SUMS')) throw new Error('缺少发布校验文件');
core.setOutput('tag', tag);
core.setOutput('current', String(tag === latest.tag_name));
if (tag !== latest.tag_name) {
core.notice(`${tag} 已被 ${latest.tag_name} 替代,跳过以免覆盖新版 Demo`);
}
- name: Check deployment configuration
if: steps.release.outputs.current == 'true'
env:
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
run: |
for name in VERCEL_ORG_ID VERCEL_PROJECT_ID VERCEL_TOKEN DEMO_URL; do
if [ -z "${!name}" ]; then
echo "::error::请先在仓库 Actions 中配置 $name,参见 README 的在线 Demo 部署说明"
exit 1
fi
done
- uses: actions/checkout@v4
if: steps.release.outputs.current == 'true'
with:
ref: refs/tags/${{ steps.release.outputs.tag }}
persist-credentials: false
- name: Verify the released commit and write deployment metadata
if: steps.release.outputs.current == 'true'
env:
EXPECTED_COMMIT: ${{ inputs.commit }}
RELEASE_TAG: ${{ steps.release.outputs.tag }}
run: |
commit=$(git rev-parse HEAD)
if [ -n "$EXPECTED_COMMIT" ] && [ "$commit" != "$EXPECTED_COMMIT" ]; then
echo "::error::发布标签已移动,拒绝部署与 Release 构建不同的提交"
exit 1
fi
mkdir -p web/public
jq -n --arg tag "$RELEASE_TAG" --arg commit "$commit" \
--arg release "https://github.com/$GITHUB_REPOSITORY/releases/tag/$RELEASE_TAG" \
'{tag: $tag, commit: $commit, release: $release, mock: true}' > web/public/demo-version.json
# 兼容首次部署尚未含此配置的历史正式版;CLI 部署不受此开关影响。
jq -n '{"$schema":"https://openapi.vercel.sh/vercel.json", framework:"nextjs", installCommand:"npm ci", buildCommand:"npm run build", git:{deploymentEnabled:false}}' > web/vercel.json
- uses: actions/setup-node@v4
if: steps.release.outputs.current == 'true'
with:
node-version: "22"
cache: npm
cache-dependency-path: web/package-lock.json
- name: Install Vercel CLI
if: steps.release.outputs.current == 'true'
run: npm install --global [email protected]
- name: Configure the dedicated demo project
if: steps.release.outputs.current == 'true'
env:
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
run: |
# CLI 以 web/ 为项目根;设置与 runner 一致的 Node 主版本。
curl --fail-with-body --silent --show-error --request PATCH \
--header "Authorization: Bearer $VERCEL_TOKEN" --header 'Content-Type: application/json' \
--data '{"framework":"nextjs","nodeVersion":"22.x","rootDirectory":null,"installCommand":"npm ci","buildCommand":"npm run build"}' \
"https://api.vercel.com/v9/projects/$VERCEL_PROJECT_ID?teamId=$VERCEL_ORG_ID" \
--output /tmp/demo-project.json
curl --fail-with-body --silent --show-error --request POST \
--header "Authorization: Bearer $VERCEL_TOKEN" --header 'Content-Type: application/json' \
--data '{"key":"NEXT_PUBLIC_MOCK","value":"1","type":"plain","target":["production"]}' \
"https://api.vercel.com/v10/projects/$VERCEL_PROJECT_ID/env?teamId=$VERCEL_ORG_ID&upsert=true" \
--output /tmp/demo-env.json
jq -e '(.failed // []) | length == 0' /tmp/demo-env.json
- name: Pull production project settings
if: steps.release.outputs.current == 'true'
working-directory: web
env:
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
run: vercel pull --yes --environment=production --token="$VERCEL_TOKEN"
- name: Build the mock demo
if: steps.release.outputs.current == 'true'
working-directory: web
env:
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
run: vercel build --prod --token="$VERCEL_TOKEN"
- name: Check latest release again before publishing
if: steps.release.outputs.current == 'true'
id: publish
uses: actions/github-script@v7
env:
RELEASE_TAG: ${{ steps.release.outputs.tag }}
with:
script: |
const { data: latest } = await github.rest.repos.getLatestRelease(context.repo);
const current = latest.tag_name === process.env.RELEASE_TAG;
core.setOutput('current', String(current));
if (!current) core.notice('构建期间已有更新的正式版发布,保留线上版本,等待新版部署');
- name: Deploy the prebuilt demo to production
if: steps.publish.outputs.current == 'true'
id: deploy
working-directory: web
env:
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
run: |
url=$(vercel deploy --prebuilt --prod --yes --token="$VERCEL_TOKEN")
# 空项目首次部署也明确绑定固定域名,后续版本沿用同一个网址。
domain="${DEMO_URL#https://}"
domain="${domain%/}"
vercel alias set "$url" "$domain" --token="$VERCEL_TOKEN"
echo "url=$url" >> "$GITHUB_OUTPUT"
echo "### Demo 已部署" >> "$GITHUB_STEP_SUMMARY"
echo "- 正式版本:${{ steps.release.outputs.tag }}" >> "$GITHUB_STEP_SUMMARY"
echo "- 部署地址:$url" >> "$GITHUB_STEP_SUMMARY"
- name: Verify production version metadata
if: steps.publish.outputs.current == 'true'
env:
RELEASE_TAG: ${{ steps.release.outputs.tag }}
run: |
# 检查公开的生产域名;Vercel 可单独保护部署专属的随机域名。
curl --fail --silent --show-error --retry 5 --retry-all-errors --retry-delay 3 \
"${DEMO_URL%/}/demo-version.json" -o /tmp/demo-version.json
jq -e --arg tag "$RELEASE_TAG" '.tag == $tag and .mock == true' /tmp/demo-version.json
+11
View File
@@ -181,6 +181,17 @@ jobs:
- name: Publish complete release
run: gh release edit "$RELEASE_TAG" --draft=false --latest
# GITHUB_TOKEN 发布的 Release 不会再次触发 release: published 工作流。
# 直接依赖发布 job,保证 ZIP、校验文件和镜像全部发布成功后才更新 Demo。
demo:
needs: [prepare, release]
uses: ./.github/workflows/demo.yml
with:
tag: ${{ needs.prepare.outputs.tag }}
commit: ${{ needs.prepare.outputs.commit }}
secrets:
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
# ④ 在原生 AMD64 / ARM64 runner 上构建和测试,再推送架构镜像。
docker:
needs: [prepare, binaries]