mirror of
https://github.com/hectorqin/reader.git
synced 2026-11-10 13:04:08 +08:00
fix(ci): 让 assets 流水线真正验证产物会被打进 APK
web-assets-ci 只跑了 build-web-assets.sh,证明脚本写完了 assets,却没证明
Gradle 会读那个目录 —— 而这两处此前就是漂移的:
- build.gradle.kts 的 assets.srcDirs 指向 android/app/web-assets,
那是没有任何脚本写入的第三个路径;真正的产物落在
android/app/src/main/assets/web-assets。于是 staged 的 bundle 不是 assets
源,APK 照常构建成功,但里面没有客户端,启动即白屏。
- 注释依赖的 README.txt 占位文件已在 c18664f 被删除,所以干净 checkout 里
这个目录根本不存在,assets.srcDirs 指向一个不存在的路径。
- build-web-assets.sh 的 rm -rf "$dest" 会把那个占位文件一起删掉,
即使它还在,每次本地构建后工作树都会变脏。
改动:
- build.gradle.kts:assets.srcDirs 指向脚本实际写入的目录,注释写准。
- 恢复 web-assets.sh 目录下的 README.txt 占位,让目录进版本库。
- build-web-assets.sh:清理产物时保留 README.txt,不再制造脏工作树。
- 新增 verify-web-assets.sh:校验产物存在且是构建出的客户端、Gradle 声明的
路径与产物路径一致、占位文件确实被 git 跟踪。
- .cnb.yml:web-assets-ci 增加该 stage,并把脚本自身 / build.gradle.kts /
assets 目录纳入 ifModify —— 改这些本来不会触发任何验证。
- PR 侧补 android-contract-checks:桥接契约与渲染词表校验不需要 JDK/SDK,
秒级,不该等到 main 的完整构建才发现。
验证:check-shell / build-web-assets / verify-web-assets 全部通过;
把 assets.srcDirs 改回错误路径可复现失败。.cnb.yml 通过语法/语义/Schema 校验。
This commit is contained in:
1 parent
f16ce0224e
commit
baf63c5b28
5 files changed
+127
-4
No files matched your search
@@ -90,9 +90,21 @@ main:
|
||||
<<: *node-env
|
||||
ifModify:
|
||||
- web/**
|
||||
# 触发条件包含脚本自身与 assets 目录布局:改这段逻辑、或改 Gradle 里
|
||||
# `assets.srcDirs` 指到哪,本来不会跑任何验证,而它坏掉的形态是 APK 里
|
||||
# 没有客户端、启动即白屏 —— 只有跑一遍才知道。
|
||||
- android/scripts/build-web-assets.sh
|
||||
- android/scripts/verify-web-assets.sh
|
||||
- android/app/build.gradle.kts
|
||||
- android/app/src/main/assets/**
|
||||
stages:
|
||||
- name: stage-for-shell
|
||||
script: sh android/scripts/build-web-assets.sh
|
||||
# 只证明脚本写出了 assets 还不够,还要证明「写出来的东西真的会被打进 APK」。
|
||||
# 脚本只知道它写到哪,Gradle 只知道哪些目录算 assets,两者之间没有任何
|
||||
# 东西把它们连起来 —— 这两条路径此前就漂移过。
|
||||
- name: verify-assets-are-packaged
|
||||
script: sh android/scripts/verify-web-assets.sh
|
||||
|
||||
# 发布镜像到制品库 `docker.cnb.cool/hectorqin/reader`。
|
||||
#
|
||||
@@ -167,3 +179,18 @@ main:
|
||||
- web/**
|
||||
stages:
|
||||
- *client-check
|
||||
|
||||
# PR 阶段只跑不依赖 JDK/SDK 的那半(秒级)。桥接契约断裂与渲染模式词表漂移
|
||||
# 是最容易出、最难在本地发现的错误 —— 两半分别用 Kotlin 和 TypeScript 写,
|
||||
# 互相没法类型检查 —— 而它们恰好都不需要编译就能查出来。完整 assembleDebug
|
||||
# 仍只在 main 的 push 上跑,PR 不该为一个 SDK 镜像等几分钟。
|
||||
- name: android-contract-checks
|
||||
<<: *node-env
|
||||
ifModify:
|
||||
- android/**
|
||||
- web/src/android-bridge.d.ts
|
||||
- web/src/formats/types.ts
|
||||
- web/src/ui/native-page.ts
|
||||
stages:
|
||||
- name: shell-contract
|
||||
script: sh android/scripts/check-shell.sh
|
||||
@@ -72,10 +72,18 @@ android {
|
||||
* and H5 clients from becoming two implementations: one bundle, two
|
||||
* hosts.
|
||||
*
|
||||
* The directory has to exist for a clean checkout to build. The
|
||||
* placeholder committed under `web-assets` is what makes that work.
|
||||
* `src/main/assets/web-assets` is where
|
||||
* android/scripts/build-web-assets.sh stages that bundle, and the
|
||||
* directory is gitignored except for a README.txt placeholder.
|
||||
*
|
||||
* The placeholder is not decoration. Without it the directory is
|
||||
* absent from a clean checkout, and the previous version of this line
|
||||
* pointed at a *third* path (`android/app/web-assets`) that nothing
|
||||
* ever created — so the staged bundle was silently not an asset
|
||||
* source, and the APK built fine with no client inside it. That is a
|
||||
* worse failure than a red build: nothing points at it.
|
||||
*/
|
||||
assets.srcDirs("src/main/assets", project.layout.projectDirectory.dir("web-assets").asFile)
|
||||
assets.srcDirs("src/main/assets", "src/main/assets/web-assets")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
This file exists so the directory it lives in is tracked by git.
|
||||
|
||||
Gradle's `assets.srcDirs` points at this directory, and a clean checkout has to
|
||||
be buildable without first running the web build. The real bundle is written
|
||||
here by android/scripts/build-web-assets.sh and is gitignored, so without a
|
||||
placeholder the directory simply does not exist after `git clone`.
|
||||
@@ -30,8 +30,12 @@ fi
|
||||
npm run build
|
||||
|
||||
echo "==> staging assets into $dest"
|
||||
rm -rf "$dest"
|
||||
# Clear the previous bundle but keep README.txt. That file is tracked, and a
|
||||
# blanket `rm -rf "$dest"` deletes it — leaving a dirty working tree after every
|
||||
# build and turning `git status` into noise that hides real changes. It is also
|
||||
# the only reason the directory exists in a clean checkout.
|
||||
mkdir -p "$dest"
|
||||
find "$dest" -mindepth 1 ! -name 'README.txt' -exec rm -rf {} +
|
||||
# Only the built output. Copying the sources would ship the whole TypeScript tree
|
||||
# inside the APK for no benefit.
|
||||
cp -R "$web_dir/dist/." "$dest/"
|
||||
|
||||
Executable
+78
@@ -0,0 +1,78 @@
|
||||
#!/bin/sh
|
||||
# Proves that the staged web bundle is actually the one Gradle will pack.
|
||||
#
|
||||
# Why this is separate from build-web-assets.sh: that script knows one fact —
|
||||
# where it writes the bundle. Gradle knows a second, independent fact — which
|
||||
# directories count as assets. Nothing in the build connects them, and a mismatch
|
||||
# fails in the quietest way possible: the APK builds successfully, ships with no
|
||||
# client in it, and shows a blank screen on launch. Worse than a red build,
|
||||
# because nothing points at it.
|
||||
#
|
||||
# The two facts have already drifted: `build.gradle.kts` declared
|
||||
# `android/app/web-assets`, which no script ever wrote to, while the bundle went
|
||||
# to `android/app/src/main/assets/web-assets` — and the placeholder the comment
|
||||
# relied on had been deleted, so a clean checkout had no assets directory at all.
|
||||
#
|
||||
# Run after build-web-assets.sh in the same job; checks the staged output plus
|
||||
# the Gradle declaration that consumes it.
|
||||
set -eu
|
||||
|
||||
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
||||
android_dir=$(dirname -- "$script_dir")
|
||||
repo_dir=$(dirname -- "$android_dir")
|
||||
dest="$android_dir/app/src/main/assets/web-assets"
|
||||
gradle_file="$android_dir/app/build.gradle.kts"
|
||||
fail=0
|
||||
|
||||
note() { printf '%s\n' "$1"; }
|
||||
err() { printf 'FAIL: %s\n' "$1" >&2; fail=1; }
|
||||
|
||||
# 1. The bundle has to be there, and it has to be the built client rather than a
|
||||
# placeholder. An empty directory passes a naive existence check and still
|
||||
# produces a white-screen APK, which is exactly the case this guards.
|
||||
note "==> checking the staged bundle exists"
|
||||
if [ ! -d "$dest" ]; then
|
||||
err "$dest does not exist; build-web-assets.sh did not stage anything"
|
||||
else
|
||||
if [ ! -f "$dest/index.html" ]; then
|
||||
err "$dest has no index.html; the APK would launch to a blank screen"
|
||||
fi
|
||||
|
||||
# Vite emits the entry bundle at a path derived from vite.config.ts
|
||||
# (`entryFileNames: assets/client.js`). Matching that exact name means a change
|
||||
# to the bundler config shows up here instead of in a blank screen.
|
||||
if [ ! -f "$dest/assets/client.js" ]; then
|
||||
err "$dest/assets/client.js is missing; check entryFileNames in web/vite.config.ts"
|
||||
fi
|
||||
|
||||
count=$(find "$dest" -type f ! -name 'README.txt' | wc -l | tr -d ' ')
|
||||
note " staged files (excluding the placeholder): $count"
|
||||
if [ "$count" -lt 2 ]; then
|
||||
err "only $count real file(s) staged; expected the built client"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 2. Gradle must declare the directory the bundle was actually written to.
|
||||
# Comparing the literal path is the point: it catches a rename on either side,
|
||||
# which a build-time smoke test would not.
|
||||
note "==> checking Gradle consumes that directory"
|
||||
if ! grep -q '"src/main/assets/web-assets"' "$gradle_file"; then
|
||||
err "$gradle_file does not list src/main/assets/web-assets in assets.srcDirs; the staged bundle would be ignored"
|
||||
fi
|
||||
|
||||
# 3. The placeholder has to be committed, or the directory is absent in a clean
|
||||
# checkout and assets.srcDirs silently points at nothing.
|
||||
note "==> checking the assets directory is tracked"
|
||||
if [ ! -f "$dest/README.txt" ]; then
|
||||
err "$dest/README.txt is missing; a clean checkout would have no assets directory"
|
||||
fi
|
||||
if ! git -C "$repo_dir" ls-files --error-unmatch \
|
||||
"android/app/src/main/assets/web-assets/README.txt" >/dev/null 2>&1; then
|
||||
err "the placeholder exists on disk but is not tracked by git; it will not survive a clone"
|
||||
fi
|
||||
|
||||
if [ "$fail" -ne 0 ]; then
|
||||
note "==> web assets checks failed"
|
||||
exit 1
|
||||
fi
|
||||
note "==> web assets checks passed"
|
||||
Reference in new issue
Block a user