mirror of
https://github.com/tiennm99/loto.git
synced 2026-10-11 12:19:05 +00:00
ci: consolidate web and android workflows into one pipeline
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.
This commit is contained in:
1 parent
dfc9f67e0c
commit
c6c839fd95
17 files changed
+321
-8681
No files matched your search
@@ -0,0 +1,28 @@
|
||||
name: Set up Android toolchain
|
||||
description: >
|
||||
Installs Node, JDK and Gradle, then restores android/ dependencies from the
|
||||
lockfile. Covers the Capacitor CLI only — the web bundle it syncs is built
|
||||
separately and must already exist at web/build.
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
# Keep in lockstep with .github/actions/setup-web.
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24'
|
||||
cache: npm
|
||||
cache-dependency-path: android/package-lock.json
|
||||
|
||||
- uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: '21'
|
||||
|
||||
- uses: gradle/actions/setup-gradle@v6
|
||||
with:
|
||||
cache-provider: basic
|
||||
|
||||
- run: npm ci
|
||||
shell: bash
|
||||
working-directory: android
|
||||
@@ -0,0 +1,24 @@
|
||||
name: Set up web toolchain
|
||||
description: >
|
||||
Installs pnpm and Node, then restores web/ dependencies from the lockfile.
|
||||
Node stays in lockstep with .github/actions/setup-android so the SvelteKit
|
||||
app is tested and bundled into the APK on the same runtime.
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
# Must precede setup-node: `cache: pnpm` needs the pnpm binary to
|
||||
# resolve the store path.
|
||||
- uses: pnpm/action-setup@v6
|
||||
with:
|
||||
package_json_file: web/package.json
|
||||
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24'
|
||||
cache: pnpm
|
||||
cache-dependency-path: web/pnpm-lock.yaml
|
||||
|
||||
- run: pnpm install --frozen-lockfile
|
||||
shell: bash
|
||||
working-directory: web
|
||||
@@ -1,59 +0,0 @@
|
||||
name: android / build debug
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'web/**'
|
||||
- 'android/**'
|
||||
- '.github/workflows/android-build-debug.yml'
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'web/**'
|
||||
- 'android/**'
|
||||
- '.github/workflows/android-build-debug.yml'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: android
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: Set up Node 22
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '22'
|
||||
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: '21'
|
||||
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@v6
|
||||
with:
|
||||
cache-provider: basic
|
||||
|
||||
- name: Install wrapper deps
|
||||
run: npm ci
|
||||
|
||||
- name: Build web + sync into Android
|
||||
run: npm run build
|
||||
|
||||
- name: Assemble debug APK
|
||||
working-directory: android/android
|
||||
run: ./gradlew :app:assembleDebug
|
||||
|
||||
- name: Upload debug APK
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: app-debug.apk
|
||||
path: android/android/app/build/outputs/apk/debug/*.apk
|
||||
archive: false
|
||||
retention-days: 7
|
||||
@@ -1,3 +1,7 @@
|
||||
# Tag-driven release. Builds standalone rather than consuming ci.yml
|
||||
# artifacts: a tag run has no upstream run to pull from, and a release
|
||||
# should be reproducible from the tagged tree alone.
|
||||
|
||||
name: android / release
|
||||
|
||||
on:
|
||||
@@ -7,40 +11,36 @@ on:
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
concurrency:
|
||||
group: android-release-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: android
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Node 22
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '22'
|
||||
- uses: ./.github/actions/setup-web
|
||||
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: '21'
|
||||
# ci.yml does not run on tags, so this is the only gate before signing.
|
||||
- name: Test web
|
||||
run: pnpm test
|
||||
working-directory: web
|
||||
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@v6
|
||||
with:
|
||||
cache-provider: basic
|
||||
# Base "" — the APK loads the bundle from the domain root.
|
||||
- name: Build web
|
||||
run: pnpm build
|
||||
working-directory: web
|
||||
|
||||
- name: Install wrapper deps
|
||||
run: npm ci
|
||||
- uses: ./.github/actions/setup-android
|
||||
|
||||
- name: Build web + sync into Android
|
||||
run: npm run build
|
||||
- name: Sync web bundle into the native project
|
||||
run: npm run sync
|
||||
working-directory: android
|
||||
|
||||
- name: Decode keystore
|
||||
env:
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
# One pipeline for both subprojects, because they ship from the same commit.
|
||||
#
|
||||
# The web app is built twice and only twice — once per base path — and every
|
||||
# consumer downloads an artifact instead of rebuilding:
|
||||
#
|
||||
# build (root, base "") -> deploy-firebase, preview-firebase, android-debug
|
||||
# build (gh, base /loto) -> deploy-pages
|
||||
#
|
||||
# Tags are handled by android-release.yml, which builds standalone.
|
||||
|
||||
name: ci
|
||||
|
||||
# Actions' parser rejects YAML anchors, so the two path lists are duplicated
|
||||
# by necessity — keep them identical.
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'web/**'
|
||||
- 'android/**'
|
||||
- '.github/actions/**'
|
||||
- '.github/workflows/ci.yml'
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'web/**'
|
||||
- 'android/**'
|
||||
- '.github/actions/**'
|
||||
- '.github/workflows/ci.yml'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ci-${{ github.ref }}
|
||||
# Superseded PRs are safe to kill; a main run may be mid-deploy.
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: ./.github/actions/setup-web
|
||||
- run: pnpm test
|
||||
working-directory: web
|
||||
|
||||
build:
|
||||
# Nothing is built, shipped or signed unless the suite is green.
|
||||
needs: test
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
# Firebase and the APK serve from a domain root.
|
||||
- profile: root
|
||||
script: build
|
||||
artifact: web-build
|
||||
# GitHub Pages serves under /loto.
|
||||
- profile: gh
|
||||
script: 'build:gh'
|
||||
artifact: web-build-gh
|
||||
name: build (${{ matrix.profile }})
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: ./.github/actions/setup-web
|
||||
- run: pnpm ${{ matrix.script }}
|
||||
working-directory: web
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: ${{ matrix.artifact }}
|
||||
path: web/build
|
||||
# Hand-off within this run only; releases keep their own copies.
|
||||
retention-days: 1
|
||||
|
||||
deploy-pages:
|
||||
if: github.event_name != 'pull_request'
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
# Guards the shared Pages environment against runs of other workflows.
|
||||
concurrency:
|
||||
group: github-pages
|
||||
cancel-in-progress: true
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
steps:
|
||||
- uses: actions/download-artifact@v7
|
||||
with:
|
||||
name: web-build-gh
|
||||
path: build
|
||||
- uses: actions/configure-pages@v6
|
||||
- uses: actions/upload-pages-artifact@v5
|
||||
with:
|
||||
path: build
|
||||
- id: deployment
|
||||
uses: actions/deploy-pages@v5
|
||||
|
||||
deploy-firebase:
|
||||
if: github.event_name != 'pull_request'
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
concurrency:
|
||||
group: firebase-hosting-live
|
||||
cancel-in-progress: false
|
||||
steps:
|
||||
# entryPoint below reads web/firebase.json and web/.firebaserc.
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/download-artifact@v7
|
||||
with:
|
||||
name: web-build
|
||||
# firebase.json declares `public: "build"`, relative to entryPoint.
|
||||
path: web/build
|
||||
- uses: FirebaseExtended/action-hosting-deploy@500ac625ca2dd40cbd15f7659af953801858032a # v0
|
||||
with:
|
||||
repoToken: ${{ secrets.GITHUB_TOKEN }}
|
||||
firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_LOOTOO }}
|
||||
channelId: live
|
||||
projectId: lootoo
|
||||
entryPoint: web
|
||||
|
||||
preview-firebase:
|
||||
# Forks cannot read the service account, so skip rather than fail.
|
||||
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
checks: write
|
||||
contents: read
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/download-artifact@v7
|
||||
with:
|
||||
name: web-build
|
||||
path: web/build
|
||||
- uses: FirebaseExtended/action-hosting-deploy@500ac625ca2dd40cbd15f7659af953801858032a # v0
|
||||
with:
|
||||
repoToken: ${{ secrets.GITHUB_TOKEN }}
|
||||
firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_LOOTOO }}
|
||||
projectId: lootoo
|
||||
entryPoint: web
|
||||
|
||||
android-debug:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/download-artifact@v7
|
||||
with:
|
||||
name: web-build
|
||||
# capacitor.config.json declares `webDir: "../web/build"`.
|
||||
path: web/build
|
||||
- uses: ./.github/actions/setup-android
|
||||
# `npm run build` would rebuild web/; the bundle is already here.
|
||||
- name: Sync web bundle into the native project
|
||||
run: npm run sync
|
||||
working-directory: android
|
||||
- name: Assemble debug APK
|
||||
run: ./gradlew :app:assembleDebug
|
||||
working-directory: android/android
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: app-debug.apk
|
||||
path: android/android/app/build/outputs/apk/debug/*.apk
|
||||
archive: false
|
||||
retention-days: 7
|
||||
@@ -1,51 +0,0 @@
|
||||
name: web / deploy to GitHub Pages
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'web/**'
|
||||
- '.github/workflows/web-deploy-github-pages.yml'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
concurrency:
|
||||
group: github-pages
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: web
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: pnpm/action-setup@v6
|
||||
with:
|
||||
package_json_file: web/package.json
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24'
|
||||
cache: pnpm
|
||||
cache-dependency-path: web/pnpm-lock.yaml
|
||||
- run: pnpm install --frozen-lockfile
|
||||
- run: pnpm build:gh
|
||||
- uses: actions/configure-pages@v6
|
||||
- uses: actions/upload-pages-artifact@v5
|
||||
with:
|
||||
path: web/build
|
||||
|
||||
deploy:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
steps:
|
||||
- id: deployment
|
||||
uses: actions/deploy-pages@v5
|
||||
@@ -1,26 +0,0 @@
|
||||
# Originally generated by the Firebase CLI
|
||||
# https://github.com/firebase/firebase-tools
|
||||
# entryPoint points the action at web/, where firebase.json and .firebaserc live.
|
||||
|
||||
name: web / deploy to Firebase Hosting on merge
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'web/**'
|
||||
- '.github/workflows/web-firebase-hosting-merge.yml'
|
||||
jobs:
|
||||
build_and_deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- run: npm ci && npm run build
|
||||
working-directory: web
|
||||
- uses: FirebaseExtended/action-hosting-deploy@v0
|
||||
with:
|
||||
repoToken: ${{ secrets.GITHUB_TOKEN }}
|
||||
firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_LOOTOO }}
|
||||
channelId: live
|
||||
projectId: lootoo
|
||||
entryPoint: web
|
||||
@@ -1,28 +0,0 @@
|
||||
# Originally generated by the Firebase CLI
|
||||
# https://github.com/firebase/firebase-tools
|
||||
# entryPoint points the action at web/, where firebase.json and .firebaserc live.
|
||||
|
||||
name: web / deploy to Firebase Hosting on PR
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'web/**'
|
||||
- '.github/workflows/web-firebase-hosting-pull-request.yml'
|
||||
permissions:
|
||||
checks: write
|
||||
contents: read
|
||||
pull-requests: write
|
||||
jobs:
|
||||
build_and_preview:
|
||||
if: ${{ github.event.pull_request.head.repo.full_name == github.repository }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- run: npm ci && npm run build
|
||||
working-directory: web
|
||||
- uses: FirebaseExtended/action-hosting-deploy@v0
|
||||
with:
|
||||
repoToken: ${{ secrets.GITHUB_TOKEN }}
|
||||
firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_LOOTOO }}
|
||||
projectId: lootoo
|
||||
entryPoint: web
|
||||
@@ -1,41 +0,0 @@
|
||||
name: web / verify build
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'web/**'
|
||||
- '.github/workflows/web-verify-build.yml'
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'web/**'
|
||||
- '.github/workflows/web-verify-build.yml'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: web-verify-build-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: web
|
||||
|
||||
jobs:
|
||||
verify:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: pnpm/action-setup@v6
|
||||
with:
|
||||
package_json_file: web/package.json
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24'
|
||||
cache: pnpm
|
||||
cache-dependency-path: web/pnpm-lock.yaml
|
||||
- run: pnpm install --frozen-lockfile
|
||||
- run: pnpm test
|
||||
- run: pnpm build
|
||||
@@ -31,17 +31,33 @@ Per-project detail lives in [`web/README.md`](web/README.md) and
|
||||
|
||||
## CI
|
||||
|
||||
Workflows live at the repository root in `.github/workflows/`; each is prefixed
|
||||
with the subproject it serves and filtered on the paths it cares about.
|
||||
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 |
|
||||
|----------|---------|--------|
|
||||
| `web-verify-build` | push/PR touching `web/` | `pnpm test && pnpm build` |
|
||||
| `web-deploy-github-pages` | push to `main` touching `web/` | publishes to GitHub Pages |
|
||||
| `web-firebase-hosting-merge` | push to `main` touching `web/` | deploys to Firebase Hosting |
|
||||
| `web-firebase-hosting-pull-request` | PR touching `web/` | Firebase preview channel |
|
||||
| `android-build-debug` | push/PR touching `web/` or `android/` | unsigned debug APK artifact |
|
||||
| `android-release` | tag `v*.*.*` | signed AAB + APK on the GH Release, plus Play Store internal-track upload |
|
||||
| `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`](docs/play-store-publishing.md).
|
||||
|
||||
+12
-6
@@ -1,6 +1,6 @@
|
||||
# Lô tô — Android (Capacitor wrapper)
|
||||
|
||||

|
||||

|
||||
|
||||
Fully-offline Android wrapper around the [`web/`](../web) SvelteKit PWA. All
|
||||
assets — HTML, JS, CSS, and 184 voice MP3s — are bundled into the APK at build
|
||||
@@ -31,7 +31,7 @@ and the `<audio>` element all work offline.
|
||||
|
||||
- Capacitor 8 (Android wrapper)
|
||||
- Web app in `web/`: SvelteKit 2 + Vite 8 + `@sveltejs/adapter-static` + `@vite-pwa/sveltekit`
|
||||
- minSdk 24 · targetSdk 36 · JDK 21 · Node 22 (Capacitor 8 requires both)
|
||||
- minSdk 24 · targetSdk 36 · JDK 21 · Node 24 (Capacitor 8 requires the first two)
|
||||
|
||||
## Setup
|
||||
|
||||
@@ -120,11 +120,17 @@ Workflows live at the repository root in `.github/workflows/`.
|
||||
|
||||
| Workflow | Trigger | Result |
|
||||
|----------|---------|--------|
|
||||
| `android-build-debug` | push to `main`, any PR touching `web/` or `android/` | unsigned debug APK uploaded as artifact |
|
||||
| `android-release` | tag `v*.*.*` | signed AAB + APK attached to GH Release |
|
||||
| `ci` (`android-debug` job) | push to `main`, any PR touching `web/` or `android/` | unsigned debug APK uploaded as artifact |
|
||||
| `android-release` | tag `v*.*.*` | tests, then signed AAB + APK attached to GH Release |
|
||||
|
||||
Both install npm deps in `android/`, build `web/`, `cap sync`, then run Gradle
|
||||
inside `android/android/`.
|
||||
Both `cap sync` a web bundle into `android/android/`, then run Gradle there.
|
||||
They differ in where the bundle comes from: the `android-debug` job downloads
|
||||
the artifact `ci` already built, so `web/` is never built twice in one run,
|
||||
while `android-release` builds `web/` itself from the tagged tree. Neither
|
||||
runs `npm run build` in `android/` — that script would rebuild `web/`.
|
||||
|
||||
Toolchain setup is shared through the composite actions
|
||||
`.github/actions/setup-web` and `.github/actions/setup-android`.
|
||||
|
||||
### GitHub Secrets (release only)
|
||||
|
||||
|
||||
+6
-3
@@ -118,9 +118,12 @@ The script auto-discovers all available `vi-*` voices and writes an updated mani
|
||||
|
||||
## Deployment
|
||||
|
||||
Deployed to GitHub Pages via `.github/workflows/web-deploy-github-pages.yml` on push
|
||||
to `main`. Build command: `pnpm build:gh` (sets `/loto` base path). Static output in
|
||||
`build/`.
|
||||
Deployed to GitHub Pages by the `deploy-pages` job in
|
||||
`.github/workflows/ci.yml` on push to `main`. Build command: `pnpm build:gh`
|
||||
(sets `/loto` base path). Static output in `build/`.
|
||||
|
||||
The same workflow also deploys the base-`""` build to Firebase Hosting and
|
||||
bundles it into the debug APK, so `web/` is built once per base path per run.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -50,8 +50,8 @@
|
||||
| `svelte.config.js` | adapter-static (HTML export), dual basePath via BUILD_PROFILE env, SvelteKit PWA plugin config. |
|
||||
| `vite.config.js` | Tailwind + SvelteKit + PWA plugins. codeserver HMR config (port, allowedHosts, hmr). |
|
||||
| `package.json` | SvelteKit 2, Svelte 5 (runes), Tailwind 4, Vite, @vite-pwa/sveltekit. Scripts: dev, dev:codeserver, build, build:gh, lint, test, test:watch. |
|
||||
| `.github/workflows/web-deploy-github-pages.yml` | Canonical deploy. On push to `main`: `npm ci && npm run build:gh`, uploads `build/` as the GitHub Pages artifact, deploys to `https://tiennm99.github.io/loto/`. |
|
||||
| `.github/workflows/web-verify-build.yml` | CI: on push/PR to main runs `npm test && npm run build` as a regression gate before the deploy job. |
|
||||
| `.github/workflows/ci.yml` | Repo-root pipeline for push/PR. `test` (`pnpm test`) gates a two-profile `build` matrix; the artifacts feed `deploy-pages` (canonical, `https://tiennm99.github.io/loto/`), `deploy-firebase`, `preview-firebase`, and `android-debug`. |
|
||||
| `.github/actions/setup-web/` | Composite action: pnpm + Node 24 + `pnpm install --frozen-lockfile` in `web/`. Shared by `ci.yml` and `android-release.yml`. |
|
||||
| `eslint.config.mjs` | ESLint 9 flat config (@eslint/js + eslint-plugin-svelte). Declares Svelte 5 rune globals. |
|
||||
| `jsconfig.json` | Path alias `$lib`, no checkJs. |
|
||||
| `.gitignore` | Excludes node_modules, build, .env.local, etc. |
|
||||
|
||||
@@ -15,9 +15,10 @@ either profile without code changes.
|
||||
|
||||
## Production Deployment — GitHub Pages
|
||||
|
||||
Canonical deploy. Wired via `.github/workflows/web-deploy-github-pages.yml`:
|
||||
on push to `main`, runs `npm run build:gh`, uploads `build/` as the
|
||||
GitHub Pages artifact, and deploys.
|
||||
Canonical deploy. Wired via the `deploy-pages` job in
|
||||
`.github/workflows/ci.yml`: on push to `main`, the `build (gh)` job runs
|
||||
`pnpm build:gh` and uploads `build/`; `deploy-pages` downloads that artifact
|
||||
and publishes it. Both are gated on the `test` job.
|
||||
|
||||
One-time setup (already done; documented for restoration):
|
||||
1. Repo → Settings → Pages → Source: **GitHub Actions**.
|
||||
@@ -134,12 +135,21 @@ Generates:
|
||||
|
||||
## CI/CD Pipeline
|
||||
|
||||
Two workflows on `main`:
|
||||
- **`.github/workflows/web-deploy-github-pages.yml`** — canonical deploy. Builds
|
||||
with `npm run build:gh` and publishes `build/` to GitHub Pages.
|
||||
- **`.github/workflows/web-verify-build.yml`** — PR + push gate. Runs
|
||||
`npm test && npm run build` to catch regressions before they reach the
|
||||
deploy job.
|
||||
One workflow, `.github/workflows/ci.yml`, covers PRs and pushes to `main`:
|
||||
|
||||
- **`test`** — `pnpm test`. Every other job depends on it, so a red suite
|
||||
blocks all deploys.
|
||||
- **`build`** — a two-entry matrix producing the only two web builds in the
|
||||
run: `pnpm build` (base `""`, artifact `web-build`) and `pnpm build:gh`
|
||||
(base `/loto`, artifact `web-build-gh`).
|
||||
- **`deploy-pages`** — canonical deploy; publishes `web-build-gh`.
|
||||
- **`deploy-firebase`** / **`preview-firebase`** — Firebase live channel on
|
||||
`main`, preview channel on same-repo PRs; both consume `web-build`.
|
||||
- **`android-debug`** — syncs `web-build` into the Capacitor project and
|
||||
assembles an unsigned APK.
|
||||
|
||||
Tags run `.github/workflows/android-release.yml` instead, which builds the
|
||||
web app itself because no `ci` run exists to take artifacts from.
|
||||
|
||||
## Performance Checklist
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ State is entirely client-side. Each card / panel instance uses a unique localSto
|
||||
|
||||
## Deployment
|
||||
|
||||
- **Production**: GitHub Pages at `https://tiennm99.github.io/loto/` (canonical, basePath `/loto`). Deployed by `.github/workflows/web-deploy-github-pages.yml` on push to `main`.
|
||||
- **Production**: GitHub Pages at `https://tiennm99.github.io/loto/` (canonical, basePath `/loto`). Deployed by the `deploy-pages` job in `.github/workflows/ci.yml` on push to `main`.
|
||||
- **Development**: `npm run dev` (local), `npm run dev:codeserver` (code-server via proxy).
|
||||
- **Build**: `npm run build:gh` generates static export to `build/` directory with the `/loto` basePath.
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ npm run build:gh
|
||||
basePath="/loto"
|
||||
Output: build/index.html with /loto prefix
|
||||
Deploy: https://tiennm99.github.io/loto/
|
||||
Workflow: .github/workflows/web-deploy-github-pages.yml
|
||||
Workflow: .github/workflows/ci.yml (build "gh" -> deploy-pages)
|
||||
```
|
||||
|
||||
### Development (Local)
|
||||
|
||||
Generated
-8423
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user