Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/ratewise-artifact-ssot.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@app/ratewise': patch
---

Clarify generated artifact buckets and remove local build report files from tracked source.
5 changes: 5 additions & 0 deletions .changeset/ratewise-currency-route-registry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@app/ratewise': patch
---

Consolidate currency landing route registration behind a registry with parity checks.
5 changes: 5 additions & 0 deletions .changeset/ratewise-data-pr-governance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@app/ratewise': patch
---

Strengthen scheduled rate data update governance by requiring generated data PRs to pass branch protection before merge.
5 changes: 5 additions & 0 deletions .changeset/ratewise-error-classification-cross-browser.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@app/ratewise': patch
---

errorClassification 補上 Firefox 與 iOS / macOS Safari 多種 fetch 失敗訊息覆蓋(離線、連線中斷、DNS 失敗、無法連線),避免一般網路失敗在跨瀏覽器下被誤分類為 unknown;同步修正刷新腳本 cache 分支幣別數量輸出。
5 changes: 5 additions & 0 deletions .changeset/ratewise-error-observability.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@app/ratewise': patch
---

改善正式版全域錯誤分類,避免一般網路錯誤被誤判為預期歷史匯率缺檔
5 changes: 5 additions & 0 deletions .changeset/ratewise-fallback-snapshot.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@app/ratewise': patch
---

讓 build-time fallback 匯率快照由每日資料更新流程維護,避免一般 build 產生匯率資料漂移;同時在線上遠端匯率來源全失敗且本機無快取時,改用 build-time snapshot 維持換算器可用。
5 changes: 5 additions & 0 deletions .changeset/ratewise-multi-rate-label.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@app/ratewise': patch
---

修正多幣別頁面在換錢所反向匯率、現金不可用與小字低對比情境下的匯率來源標籤。
5 changes: 5 additions & 0 deletions .changeset/ratewise-production-surface.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@app/ratewise': patch
---

正式版不再輸出內部展示與測試頁面的預渲染路由
5 changes: 5 additions & 0 deletions .changeset/ratewise-ssg-snapshot-source.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@app/ratewise': patch
---

修正金額頁 SSG 預渲染匯率來源,clean checkout 不再依賴 ignored 的 public/rates.json。
53 changes: 53 additions & 0 deletions .github/workflows/ratewise-production-governance.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: RateWise Production Governance

on:
workflow_dispatch:
schedule:
- cron: '17 20 * * *'

permissions:
contents: read

jobs:
production-governance:
name: Live Headers And Performance Gates
runs-on: ubuntu-latest
timeout-minutes: 20
env:
PLAYWRIGHT_BASE_URL: https://app.haotool.org
E2E_BASE_PATH: /ratewise

steps:
- name: Checkout code
uses: actions/checkout@v6

- name: Setup pnpm
uses: pnpm/action-setup@v6
with:
version: 9.10.0

- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '24'
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Install Playwright Chromium
run: pnpm --filter @app/ratewise exec playwright install --with-deps chromium

- name: Verify Cloudflare headers and cache policy
run: |
RUN_PRODUCTION_TESTS=true \
pnpm --filter @app/ratewise exec playwright test \
tests/e2e/cloudflare-cache.spec.ts \
--project=chromium-desktop

- name: Verify trend chart latency budget
run: |
RUN_RATEWISE_PERFORMANCE_TESTS=true \
pnpm --filter @app/ratewise exec playwright test \
tests/e2e/trend-chart-latency.spec.ts \
--project=chromium-desktop
22 changes: 11 additions & 11 deletions .github/workflows/update-seo-rate-examples.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# SEO 匯差範例數據每日更新
#
# 職責:每日抓取最新臺灣銀行牌告匯率,計算各幣別現金匯率與市場中間價差距
# 更新 apps/ratewise/src/config/generated/seo-rate-examples.ts
# 職責:每日抓取最新臺灣銀行牌告匯率,更新 runtime fallback snapshot
# 並計算各幣別現金匯率與市場中間價差距
#
# 流程(業界最佳實踐 — PR-based,不直接 push main):
# 1. 執行腳本(雙重驗證:台銀牌告 + open.er-api.com)
# 2. 若有變更,peter-evans/create-pull-request@v8 建立 PR 分支
# 3. PR 建立後直接 squash merge 至 main
# 3. 交由 branch protection / required checks / maintainer review 或 GitHub auto-merge 合併
# 4. 無變更時不建立 PR,靜默略過
#
# 為何不直接 push main:
Expand Down Expand Up @@ -51,6 +51,9 @@ jobs:
- name: 安裝依賴
run: pnpm install --frozen-lockfile

- name: 更新 build-time fallback 匯率 snapshot
run: pnpm --filter @app/ratewise refresh:fallback-rates

- name: 執行匯差範例更新腳本(雙重驗證模式)
run: node apps/ratewise/scripts/update-seo-rate-examples.mjs

Expand All @@ -64,6 +67,7 @@ jobs:
commit-message: |
chore(seo): 每日更新匯差範例數據

- 自動更新 build-time fallback 匯率 snapshot
- 自動更新各幣別現金賣出 vs 市場中間價差距(換 3 萬台幣情境)
- 雙重驗證:open.er-api.com 中間價 vs 台銀自身 (買入+賣出)/2 中間價
- 差距超過 2% 幣別已警告(東南亞幣別預期偏高)
Expand All @@ -73,7 +77,7 @@ jobs:
delete-branch: true
title: 'chore(seo): 每日更新匯差範例數據'
body: |
## 自動更新:SEO 匯差範例數據
## 自動更新:RateWise 匯率 fallback 與 SEO 匯差範例數據

由 GitHub Actions 排程每日自動執行。腳本:[`update-seo-rate-examples.mjs`](apps/ratewise/scripts/update-seo-rate-examples.mjs)

Expand All @@ -91,21 +95,17 @@ jobs:

### 自動化

- PR 建立後 **直接 squash merge** 至 `main`
- PR 建立後交由 branch protection、required checks 與 maintainer review 控制合併
- 若 repo 啟用 GitHub auto-merge / merge queue,應由 GitHub 在條件滿足後合併
- 無變更時不建立 PR,靜默略過
- 幣別資料缺漏時腳本 `exit(1)`,工作流程明確失敗
labels: |
automated
seo
add-paths: |
apps/ratewise/src/config/generated/build-time-rates.json
apps/ratewise/src/config/generated/seo-rate-examples.ts

- name: 合併 PR(squash)
if: steps.create-pr.outputs.pull-request-number != ''
run: gh pr merge --squash "${{ steps.create-pr.outputs.pull-request-number }}"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: 無變更
if: steps.create-pr.outputs.pull-request-number == ''
run: echo "ℹ️ 匯差範例數據無變更,略過建立 PR。"
10 changes: 10 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -460,9 +460,19 @@ git push origin main # pre-push 自動驗證

- `public/*.md`(markdown mirrors)版本號由完整 build 更新;`pnpm changeset:version` 不觸發此步驟,故 mirrors 版本會暫時落後一個版本。
- `src/config/generated/`(build-time-rates.json、seo-rate-examples.ts)由每日 SEO 排程更新。
- `apps/ratewise/lighthouse-report.json` 與 `apps/ratewise/*.tsbuildinfo` 屬本機工具輸出;已由 `.gitignore` 管理,必須保持 untracked。
- **MUST NOT**:把上述兩類修改單獨建立 release 後的 follow-up commit,`verify-version-ssot` 會因為 staged set 內沒有 version bump 或 changeset 而擋下。
- **MUST**:commit 失敗後必須重新執行 `git restore --staged --worktree <files>` 再重試;lint-staged 的 stash/restore 循環會把失敗前的 working tree 狀態還原,使已 restore 的修改重新出現。

### RateWise Generated Artifact Buckets(SSOT)

- `pnpm --filter @app/ratewise refresh:data`:更新 live snapshots(build-time rates、SEO rate examples、rating snapshot)。
- `pnpm --filter @app/ratewise refresh:fallback-rates`:只更新 committed runtime fallback rate snapshot。
- `pnpm --filter @app/ratewise generate:deterministic`:由 repo SSOT 重建 sitemap、manifest、offline shell、LLMs text、Markdown mirrors、API JSON 與 OpenAPI。
- `pnpm --filter @app/ratewise verify:artifacts`:執行 SSOT sync 與 image resource 檢查。
- `pnpm --filter @app/ratewise prebuild`:只執行 deterministic generation、artifact verification 與 rating placeholder refresh;禁止把 tracked live rate refresh 塞回單一長命令。
- `update-seo-rate-examples.yml`:只建立資料更新 PR;合併必須交由 branch protection、required checks、review 或 GitHub auto-merge / merge queue 控制,workflow 不得直接執行 `gh pr merge`。

### Release PR 自動化失敗治理

**觸發條件**:main 累積 `.changeset/*.md`,但 package version / CHANGELOG 長期未更新;或 `Release` workflow 顯示 success 但未建立 `changeset-release/main` PR。
Expand Down
13 changes: 13 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,17 @@ git push origin main # pre-push 自動跑 typecheck + test + build

禁止:手動改版號、單獨跑 prebuild scripts、直接改 CHANGELOG 跳過 changeset。

**RateWise generated artifact buckets**:

- `pnpm --filter @app/ratewise refresh:data`:live snapshots
(`build-time-rates.json`、`seo-rate-examples.ts`、`rating-snapshot.ts`)。
- `pnpm --filter @app/ratewise refresh:fallback-rates`:只更新 committed runtime fallback rate snapshot。
- `pnpm --filter @app/ratewise generate:deterministic`:repo SSOT 可重建產物
(sitemap、manifest、offline shell、LLMs text、Markdown mirrors、API JSON、OpenAPI)。
- `pnpm --filter @app/ratewise verify:artifacts`:SSOT sync 與 image resource 檢查。
- `prebuild` 只執行 deterministic generation、artifact verification 與 rating placeholder refresh;不得刷新 tracked live rate data。`lighthouse-report.json`、`*.tsbuildinfo` 屬本機工具輸出,必須保持 untracked。
- `update-seo-rate-examples.yml` 只建立資料更新 PR;合併交由 branch protection、required checks、review 或 GitHub auto-merge / merge queue 控制,workflow 不得直接執行 `gh pr merge`。

**Release PR 自動化控制**:

- `changesets/action` 的 release commit 必須使用 commitlint 豁免格式:`chore(release): 更新版本套件`
Expand Down Expand Up @@ -277,6 +288,8 @@ gh pr merge <PR_NUMBER> --squash --delete-branch=false

**發版後 `public/*.md` 或 generated 檔案觸發 SSOT 守門失敗**:`pnpm changeset:version` 只更新 api/latest.json 等 SSOT 產出物,不重新生成 markdown mirrors(`public/*.md`);若這些修改殘留並另行 commit,`verify-version-ssot` 會因新 staged set 缺少 version bump 或 changeset 而擋下。修法:`git restore --staged --worktree apps/ratewise/public/*.md apps/ratewise/src/config/generated/`,讓 CI build 與每日 SEO 排程重新生成。

**本機 build / QA 產物出現在 git status**:`apps/ratewise/lighthouse-report.json` 與 `apps/ratewise/*.tsbuildinfo` 是工具輸出,不是 source。若它們被重新建立,保持 untracked;若意外 staged,執行 `git restore --staged <file>`。

**lint-staged stash/restore 循環復活已 restore 的檔案**:commit 失敗時 lint-staged 會還原其 stash,可能把已 `git restore` 的 working tree 修改重新帶回。修法:每次 commit 失敗後必須重新執行 `git restore --staged --worktree <files>` 再重試,不可假設檔案狀態與 restore 後相同。

### PWA
Expand Down
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ haotool-app/
│ ├── release.yml # 版本發布
│ ├── seo-audit.yml # SEO 審查
│ ├── seo-production.yml # 生產環境 SEO
│ ├── ratewise-production-governance.yml # RateWise 生產治理檢查
│ ├── update-committed-seo-files.yml # SEO 產出物同步
│ ├── update-historical-rates.yml # 歷史匯率更新
│ ├── update-latest-rates.yml # 最新匯率更新
Expand Down Expand Up @@ -270,7 +271,7 @@ haotool Apps is a professional pnpm Monorepo containing multiple high-quality Re
- **Styling**: Tailwind CSS 3.4
- **Testing**: Vitest 4.1 + Playwright 1.57
- **Package Manager**: pnpm 9.10.0 (Monorepo)
- **CI/CD**: GitHub Actions (9 workflows)
- **CI/CD**: GitHub Actions (10 workflows)
- **Deployment**: Docker + Zeabur / Vercel
- **Security**: Gitleaks CLI + Trivy + SARIF

Expand Down Expand Up @@ -298,6 +299,15 @@ Production is deployed by Zeabur from GitHub main. Before merging a release PR
right after another main PR, confirm the earlier production deployment has
finished so an older SHA cannot become active after the release SHA.

### Generated Artifacts

RateWise generated files are bucketed by package scripts:
`prebuild` runs deterministic generation and verification only,
`refresh:data` explicitly updates live snapshots, and
`refresh:fallback-rates` is the only script that writes the committed fallback
rate snapshot. Local tool output such as `lighthouse-report.json` and
`*.tsbuildinfo` must remain untracked.

### License

This project is licensed under [GPL-3.0](./LICENSE).
Expand Down
20 changes: 19 additions & 1 deletion apps/ratewise/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,28 @@ RateWise 正式站由 Zeabur production deployment 發布,Release 完成後需
precache 驗證。若 GitHub Release 已建立但正式站仍回舊版,先查 GitHub deployments
的 active SHA,再以 app 範圍 PR 重新觸發最新 main 部署。

## 🧱 Generated Artifact SSOT

RateWise 將 build 產物分成三類,避免 live data、deterministic artifacts 與本機 QA
報告混在同一個 commit:

- `pnpm --filter @app/ratewise refresh:data`:更新 live data snapshot
(`build-time-rates.json`、`seo-rate-examples.ts`、`rating-snapshot.ts`)。
- `pnpm --filter @app/ratewise refresh:fallback-rates`:只更新已提交的 runtime
fallback 匯率 snapshot,供首次離線或 LHCI fallback 使用。
- `pnpm --filter @app/ratewise generate:deterministic`:由 repo SSOT 重建 sitemap、
manifest、offline shell、LLMs text、Markdown mirrors、API JSON 與 OpenAPI。
- `pnpm --filter @app/ratewise verify:artifacts`:驗證 SSOT sync 與圖片資源。

`prebuild` 只執行 deterministic generation、artifact verification 與 rating placeholder
refresh,不會刷新 tracked live rate data。`lighthouse-report.json` 與 `*.tsbuildinfo` 屬本機工具輸出,
必須保持 untracked。QA 截圖集中放 `screenshots/`,正式 SEO/manifest 圖片則保留在
`public/screenshots/`。

## 📄 授權

GPL-3.0 © [haotool](https://app.haotool.org/)

---

**最後更新**: 2026-04-28
**最後更新**: 2026-05-12
Loading
Loading