Both subprojects ship from the same commit, so ordinary CI is now a single ci.yml; only the tag-driven release stands apart. The web app is built twice per run — once per base path — and every consumer downloads the artifact instead of rebuilding, replacing three redundant base-"" builds on main. Nothing deploys unless the test job is green, and android-release runs the suite before signing (ci.yml does not fire on tags, so it was the only gap). Shared toolchain setup moves into composite actions, which puts the web build and the APK on the same Node version for the first time. The Firebase PR path was still on npm ci against a stale web/package-lock.json that could resolve a different tree than pnpm-lock.yaml; drop the lockfile and the npm path with it. Also: least-privilege permissions widened per job, persist-credentials off on every checkout, concurrency groups that cancel superseded PRs but never a live deploy, npm caching for android, and the Firebase action pinned by commit SHA to match how the release actions were already pinned.
2.9 KiB
Lô Tô
Lô tô hội chợ — Vietnamese fairground bingo. One number caller, two shipping targets: a static web app and a fully-offline Android APK that wraps it.
Live: tiennm99.github.io/loto
Layout
| Path | What it is |
|---|---|
web/ |
SvelteKit static app — the game itself. pnpm, Vitest, deployed to GitHub Pages and Firebase Hosting. |
android/ |
Capacitor wrapper that bundles web/build into an APK. npm + Gradle; the native project sits in android/android/. |
android/ builds web/ as part of its own build, so a change under web/
reaches the APK on the next npm run build in android/. There is no version
pin between them — they ship from the same commit.
Quick start
# web
cd web && pnpm install && pnpm dev
# android debug APK (builds web/ first)
cd android && npm ci && npm run build && npm run assemble:debug
Per-project detail lives in web/README.md and
android/README.md.
CI
Workflows live at the repository root in .github/workflows/. Because both
subprojects ship from the same commit, ordinary CI is a single ci.yml;
only the tag-driven release stands apart.
| Workflow | Trigger | Result |
|---|---|---|
ci |
push to main or PR touching web/ or android/ |
see the job graph below |
android-release |
tag v*.*.* |
tests, then a signed AAB + APK on the GH Release, plus Play Store internal-track upload |
ci builds the web app exactly twice — once per base path — and every
consumer downloads the artifact rather than rebuilding it:
test (pnpm test)
└── build (root, base "") ── deploy-firebase push to main
│ ├─ preview-firebase PR from this repo
│ └─ android-debug unsigned APK artifact
└── build (gh, base /loto) ── deploy-pages push to main
Nothing deploys unless test is green. Deploy jobs are skipped on pull
requests, and the Firebase preview is skipped for PRs from forks, which
cannot read the service-account secret.
Shared toolchain setup lives in .github/actions/setup-web and
.github/actions/setup-android — composite actions used by both workflows,
so Node stays on one version across the web build and the APK.
Release/secret setup for the Play Store pipeline is documented in
docs/play-store-publishing.md.
History
This repository is the merge of the former tiennm99/loto (now web/) and
tiennm99/loto-android (now android/). Both histories are preserved commit
for commit and interleaved in commit-time order under a single root, so
git log reads as one timeline and git log -- web/ or git log -- android/
each resolve on the current paths.
License
Apache-2.0 — see LICENSE.