H5 离线包 & 外部落地页 · 发布流程
一次提交 · 三个制品
同一个 Git commit、同一条流水线,产出三个独立发布、但同源的制品。三者是同一份 H5 代码的不同交付形态。
| 制品 | 面向 | 触达方式 | 发布节奏 |
|---|---|---|---|
| INTERNAL 离线包 | App 内 H5(内部包) | 仅 Native 版本 88.88.88 | 登记 DRAFT → 扫码验收 → 发布(人工) |
| RELEASE 离线包 | App 内 H5(线上包) | 其余所有 Native 版本 | 登记 DRAFT → 灰度 → 发布(人工) |
| PUBLIC_LANDING 落地页 | 浏览器(无 App 时) | WA 裂变/分享打开的脱离式落地页 | 先发测试环境验收 → 手动发线上 |
流水线
.yunxiao/h5-id-offline-package-channels.yml(pipeline 4987658)→ npm run deploy:oss-cdn:channels → scripts/deploy-h5-package-channels.sh:构建 INTERNAL + RELEASE 离线包(登记为 DRAFT),并把落地页发到测试环境。制品与去向对照
| 制品 | OSS 桶 | 对象 / 域名 | 连接后端 | 是否含调试/内部配置 |
|---|---|---|---|---|
| INTERNAL | beex-id-test-h5 | packages/internal/<ver>/beex-h5.zip | 内嵌 生产 + 测试 配置 | 是(88 内部包,含文案审校/调试) |
| RELEASE | beex-id-prod-h5 | packages/release/<ver>/beex-h5.zip | 仅 生产配置 | 否 |
| 落地页 · test | beex-id-test-h5 | h5-id-test.beexofficial.com 网站根 | api-id-test | 否 |
| 落地页 · prod | beex-id-prod-h5 | h5-id.beexofficial.com 网站根 | api-id | 否 |
注意去向重叠:离线包 zip 和落地页网站根都放在同一批桶里,但路径/角色完全不同——
packages/…/beex-h5.zip 是 App 离线包;桶根的 index.html + /_nuxt 是浏览器落地页。发离线包不动落地页,发落地页不动离线包。离线包:构建 + 登记(DRAFT)
流水线按渠道各构建一次并上传 zip,然后调用 admin 登记接口。登记接口只允许创建 DRAFT,不能直接发布。
- 渠道选择由 Native 版本决定,不是业务环境:
88.88.88→ INTERNAL(envCode=id-test,min/max native 都锁 88.88.88);其余版本 → RELEASE(envCode=id)。 - 每个包的
manifest.json带packageChannel(INTERNAL / RELEASE),供 App 侧强校验。 - 登记:
POST /api/v1/admin/h5-packages(INTERNAL 发到admin-api-id-test,RELEASE 发到admin-api-id),服务端返回 DRAFT。
为什么不能"流水线一键发布":登记接口显式拒绝
PUBLISHED——"H5 包登记接口只允许创建 DRAFT,测试、灰度和发布必须使用对应的发布动作"。发布是刻意分离的人工门禁(见下)。离线包:发布门禁(灰度 / 验收 → 上线)
在 admin 后台(beex-admin-page 的 H5 发布页)由管理员操作,需飞书 OAuth 管理员会话(登记走 token 旁路,发布/灰度动作不行)。
生产包(RELEASE,id):
测试包(INTERNAL,id-test):
DRAFT → 灰度 gray-release(GRAY) → 发布 publish(PUBLISHED)
测试包(INTERNAL,id-test):
DRAFT → 扫码验收(TEST_PASSED) → 发布 publish
- 接口:
POST /api/v1/admin/h5-packages/{id}/gray-release、POST /api/v1/admin/h5-packages/{id}/publish。publish会强校验前置状态(非 GRAY / TEST_PASSED 直接报错)。 - verify 门禁:以
PUBLISHED发布时,流水线verify-published-channels.sh会拉两个渠道的/latest包、校验其manifest.packageChannel,缺失/不匹配即阻断发布。 - 发布后
rolloutPercent(默认 100)决定放量比例;进入/latest后 App 下次检查即可获取。
只有登记(DRAFT)不等于上线。真正让用户拿到,必须走上面的灰度/验收 + 发布动作。
离线包:App 侧选渠道与校验
- App 用
GET /api/v1/app/h5-package/latest?countryCode&envCode&platform&nativeVersion&buildNumber&deviceId拉最新包;envCode由 App 按 Native 版本推导(88.88.88 →id-test,其余 →id)。 - App 严格校验下载包
manifest.packageChannel是否与期望渠道一致;缺packageChannel的历史包按仓库分区兼容放行(不再硬拒收),有明确渠道但不匹配则拒收。 - 校验失败(下载/渠道不符)→ 回退内置包,不影响启动。
落地页:是什么
三个页面,同一个 Nuxt SPA 里的 hash 路由(PUBLIC_LANDING 构建只保留这三条路由、剥掉完整 App 页面):
| 页面 | 路由 | 场景 |
|---|---|---|
| 邀请 | /#/invite/<code> | 邀请裂变落地 |
| 商品分享 | /#/share/<code> | 商品分享落地 |
| 蜜源圈 | /#/honey/<code> | 内容/爆品分享落地 |
- 用 RELEASE 渠道构建(固定正式 API 口径、无调试入口/内部配置)+
H5_PUBLIC_LANDING=true(Web 绝对资源路径/_nuxt/、hash 路由、只三页)。 - 直接把构建产物传到 OSS 网站根 + 刷 CDN,没有 DRAFT/发布状态,传即生效(与离线包的门禁不同)。
落地页:test → prod 门禁
先到测试环境验收通过,才能发布到线上环境。
流水线每次部署 → 落地页只发 测试环境(
验收通过后(人工一步) →
h5-id-test.beexofficial.com,连 api-id-test)
验收通过后(人工一步) →
bash scripts/publish-public-landing.sh prod → 线上(h5-id.beexofficial.com,连 api-id)
- 脚本
scripts/publish-public-landing.sh <test|prod>:按目标环境覆盖后端 API 与桶/CDN 后构建并上传。 - 流水线里
deploy_public_landing默认H5_PUBLIC_LANDING_ENV=test——所以常规部署不会碰线上落地页。 nuxt.config用H5_PUBLIC_LANDING_API_BASE/H5_PUBLIC_LANDING_SITE_URL覆盖「生产环境」的 API/站点,让apiBase与environmentConfigs都指向所选后端。
已验证:流水线部署后测试站
h5-id-test 更新且连 api-id-test;线上站 h5-id 保持不变——门禁生效。落地页:WhatsApp 分享承接(OG 预览)
分享到 WhatsApp 的应是 www.beexofficial.com 下的服务端短链,由服务端渲染 Open Graph(商品图、标题)后再 302 跳到 H5 落地页。
WhatsApp 消息里的链接 →
www.beexofficial.com/id/s/<code>(或 /invite/…、蜜源圈)→ 服务端渲染 OG + 302 → h5-id.beexofficial.com/#/… 落地页
- 服务端承接已在
beex-service:AffiliateSharePageController(/id/s/{code})、InviteShareService(/invite/{code})、HoneyFeedPageController。
不要直接分享带
# 的 H5 地址(如 h5-id.beexofficial.com/#/share/CODE):# 后的路由服务端读不到,WhatsApp 抓不到具体内容 → 预览是空的、没有商品图/标题。环境 / 桶 / CDN 映射(ID)
| 用途 | OSS 桶 | 域名 | 后端 API | Admin API(登记) |
|---|---|---|---|---|
| 离线包 INTERNAL | beex-id-test-h5 | h5-id-test.beexofficial.com | — | admin-api-id-test |
| 离线包 RELEASE | beex-id-prod-h5 | h5-id.beexofficial.com | — | admin-api-id |
| 落地页 test | beex-id-test-h5 | h5-id-test.beexofficial.com | api-id-test | — |
| 落地页 prod | beex-id-prod-h5 | h5-id.beexofficial.com | api-id | — |
命令速查
构建 + 登记两个离线包(DRAFT)+ 落地页发测试
# 触发云效流水线(自动 push 当前 commit)
bash scripts/run-yunxiao-pipeline.sh --push
# 产出:INTERNAL/RELEASE 离线包登记为 DRAFT;落地页发到 h5-id-test(测试)
落地页:验收通过后发线上
bash scripts/publish-public-landing.sh prod # 发 h5-id.beexofficial.com(连 api-id)
bash scripts/publish-public-landing.sh test # 仅重发测试站(默认)
离线包:正式发布(admin 后台人工)
#(在 beex-admin-page H5 发布页,需飞书管理员会话)
# 生产包:DRAFT → 灰度(gray-release) → 发布(publish)
# 测试包:DRAFT → 扫码验收(TEST_PASSED) → 发布(publish)
发布前自检两个渠道 latest 是否带 packageChannel
bash scripts/verify-published-channels.sh https://api-id.beexofficial.com ID
常见坑
- DRAFT ≠ 上线。离线包跑完流水线只是登记 DRAFT,必须再走灰度/验收 + 发布动作,用户才拿得到。
- 落地页传即生效、无灰度。所以引入了 test → prod 门禁:常规流水线只发测试站,线上要单独
publish-public-landing.sh prod。历史上落地页曾因"流水线只发离线包、跳过网站根"而长期停在旧版。 - 缺
packageChannel的历史包。升级后的 App 对包做严格渠道校验;历史遗留包(manifest 无 packageChannel)需用deploy-h5-package-channels.sh重新构建发布带上渠道标记。 - 分享一定走
www服务端短链,别用#/直连,否则 WhatsApp 无 OG 预览。 - 离线包 vs 网站根同桶不同路径。别把
packages/…/beex-h5.zip(App 包)和桶根index.html(落地页)搞混。