forked from workadventure/workadventure
-
Notifications
You must be signed in to change notification settings - Fork 9
feat(mobile): add app update management #14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
firstedition0123
wants to merge
7
commits into
BAWES-Universe:universe
Choose a base branch
from
firstedition0123:feat/mobile-update-management-codex-on-scaffold
base: universe
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
553eeed
feat(mobile): add Capacitor scaffold pointing at universe.bawes.net
BAWES dceb2b0
fix(mobile): address all CodeRabbit review issues
BAWES 627b7a6
fix(mobile): convert config to .js, fix CI tsc failure
BAWES 4e1b044
feat(mobile): add update management
firstedition0123 e3b1c25
fix(mobile): address update prompt review
firstedition0123 d320195
fix(mobile): address update review feedback
firstedition0123 3213ecd
fix(mobile): address update review follow-up
firstedition0123 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| name: Mobile CI | ||
|
|
||
| # Validates the Capacitor config and Fastlane setup on every PR | ||
| # that touches the mobile/ folder. | ||
|
|
||
| on: | ||
| pull_request: | ||
| branches: [universe] | ||
| paths: | ||
| - "mobile/**" | ||
| - ".github/workflows/mobile-ci.yml" | ||
| push: | ||
| branches: [universe] | ||
| paths: | ||
| - "mobile/**" | ||
| - ".github/workflows/mobile-ci.yml" | ||
|
|
||
| jobs: | ||
| validate: | ||
| name: Validate Capacitor config | ||
| runs-on: ubuntu-latest | ||
| defaults: | ||
| run: | ||
| working-directory: mobile | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: "20" | ||
| # No npm install needed — config is plain JS with no imports | ||
|
|
||
| - name: Verify capacitor config is valid JS | ||
| run: node -e "const c = require('./capacitor.config.js'); if (!c.appId || !c.server || !c.server.url) { console.error('Invalid config:', c); process.exit(1); } console.log('Config OK — appId:', c.appId, 'url:', c.server.url);" | ||
| # Validates the file parses, exports an object, and has required fields. | ||
| # No @capacitor/cli install needed — plain node require() is sufficient. | ||
|
|
||
| - name: Run cap doctor (info only) | ||
| run: npx --yes @capacitor/cli@8.3.4 doctor 2>&1 | ||
| continue-on-error: true | ||
| # cap doctor warns about missing Android/iOS platforms — expected at | ||
| # scaffold stage. continue-on-error makes the intent explicit. | ||
|
|
||
| fastlane-setup: | ||
| name: Validate Gemfile / Fastlane install | ||
| runs-on: ubuntu-latest | ||
| defaults: | ||
| run: | ||
| working-directory: mobile | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - uses: ruby/setup-ruby@v1 | ||
| with: | ||
| ruby-version: "3.2" | ||
| bundler-cache: true | ||
| working-directory: mobile | ||
|
|
||
| - name: Confirm fastlane installed | ||
| run: bundle exec fastlane --version | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| import type { Express, Request, Response } from "express"; | ||
| import { getMobileVersionPayload } from "../Services/MobileVersionService"; | ||
|
|
||
| export class VersionController { | ||
| constructor(private app: Express) { | ||
| this.app.get("/api/version", this.version.bind(this)); | ||
| } | ||
|
|
||
| private version(req: Request, res: Response): void { | ||
| res.status(200).json(getMobileVersionPayload()); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| import { | ||
| MOBILE_ANDROID_UPDATE_URL, | ||
| MOBILE_IOS_UPDATE_URL, | ||
| MOBILE_LATEST_NATIVE_VERSION, | ||
| MOBILE_MIN_NATIVE_VERSION, | ||
| MOBILE_WEB_VERSION, | ||
| } from "../Enum/EnvironmentVariable"; | ||
|
|
||
| export interface MobileVersionPayload { | ||
| webVersion: string; | ||
| minNativeVersion: string; | ||
| latestNativeVersion: string; | ||
| updateUrl: { | ||
| android: string; | ||
| ios?: string; | ||
| }; | ||
| } | ||
|
|
||
| export function getMobileVersionPayload(): MobileVersionPayload { | ||
| const updateUrl: MobileVersionPayload["updateUrl"] = { | ||
| android: MOBILE_ANDROID_UPDATE_URL, | ||
| }; | ||
|
|
||
| if (MOBILE_IOS_UPDATE_URL) { | ||
| updateUrl.ios = MOBILE_IOS_UPDATE_URL; | ||
| } | ||
|
|
||
| return { | ||
| webVersion: MOBILE_WEB_VERSION, | ||
| minNativeVersion: MOBILE_MIN_NATIVE_VERSION, | ||
| latestNativeVersion: MOBILE_LATEST_NATIVE_VERSION, | ||
| updateUrl, | ||
| }; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,116 @@ | ||
| import type { Express, Request, Response } from "express"; | ||
| import { afterEach, describe, expect, it, vi } from "vitest"; | ||
| import type { VersionController as VersionControllerType } from "../src/Controller/VersionController"; | ||
|
|
||
| const mobileEnvKeys = [ | ||
| "PLAY_URL", | ||
| "MOBILE_WEB_VERSION", | ||
| "MOBILE_MIN_NATIVE_VERSION", | ||
| "MOBILE_LATEST_NATIVE_VERSION", | ||
| "MOBILE_ANDROID_UPDATE_URL", | ||
| "MOBILE_IOS_UPDATE_URL", | ||
| ] as const; | ||
|
|
||
| interface VersionResponse { | ||
| webVersion: string; | ||
| minNativeVersion: string; | ||
| latestNativeVersion: string; | ||
| updateUrl: { | ||
| android: string; | ||
| ios?: string; | ||
| }; | ||
| } | ||
|
|
||
| async function loadVersionController(): Promise<typeof VersionControllerType> { | ||
| vi.resetModules(); | ||
| process.env.PLAY_URL = "http://play.workadventure.localhost"; | ||
| const { VersionController } = await import("../src/Controller/VersionController"); | ||
| return VersionController; | ||
| } | ||
|
|
||
| function createRouteHarness(): { | ||
| routes: Map<string, (req: Request, res: Response) => void>; | ||
| app: Express; | ||
| status: ReturnType<typeof vi.fn>; | ||
| json: ReturnType<typeof vi.fn<(body: VersionResponse) => void>>; | ||
| res: Response; | ||
| } { | ||
| const routes = new Map<string, (req: Request, res: Response) => void>(); | ||
| const app = { | ||
| get: (path: string, handler: (req: Request, res: Response) => void) => { | ||
| routes.set(path, handler); | ||
| }, | ||
| } as unknown as Express; | ||
| const status = vi.fn().mockReturnThis(); | ||
| const json = vi.fn<(body: VersionResponse) => void>(); | ||
| const res = { status, json } as unknown as Response; | ||
|
|
||
| return { routes, app, status, json, res }; | ||
| } | ||
|
|
||
| afterEach(() => { | ||
| for (const key of mobileEnvKeys) { | ||
| delete process.env[key]; | ||
| } | ||
| }); | ||
|
|
||
| describe("VersionController", () => { | ||
| it("exposes mobile update metadata", async () => { | ||
| const VersionController = await loadVersionController(); | ||
| const { routes, app, status, json, res } = createRouteHarness(); | ||
|
|
||
| new VersionController(app); | ||
| routes.get("/api/version")?.({} as Request, res); | ||
|
|
||
| expect(status).toHaveBeenCalledWith(200); | ||
| const payload = json.mock.calls[0]?.[0]; | ||
| expect(payload).toMatchObject({ | ||
| webVersion: "dev", | ||
| minNativeVersion: "1.0.0", | ||
| latestNativeVersion: "1.0.0", | ||
| updateUrl: { | ||
| android: "https://play.google.com/store/apps/details?id=net.bawes.universe", | ||
| }, | ||
| }); | ||
| expect(payload?.updateUrl).not.toHaveProperty("ios"); | ||
| }); | ||
|
|
||
| it("exposes configured mobile update metadata", async () => { | ||
| process.env.MOBILE_WEB_VERSION = "2026.05.15"; | ||
| process.env.MOBILE_MIN_NATIVE_VERSION = "1.2.3"; | ||
| process.env.MOBILE_LATEST_NATIVE_VERSION = "1.4.0"; | ||
| process.env.MOBILE_ANDROID_UPDATE_URL = "https://play.google.com/store/apps/details?id=net.bawes.custom"; | ||
| process.env.MOBILE_IOS_UPDATE_URL = "https://apps.apple.com/app/id1234567890"; | ||
| const VersionController = await loadVersionController(); | ||
| const { routes, app, status, json, res } = createRouteHarness(); | ||
|
|
||
| new VersionController(app); | ||
| routes.get("/api/version")?.({} as Request, res); | ||
|
|
||
| expect(status).toHaveBeenCalledWith(200); | ||
| expect(json).toHaveBeenCalledWith({ | ||
| webVersion: "2026.05.15", | ||
| minNativeVersion: "1.2.3", | ||
| latestNativeVersion: "1.4.0", | ||
| updateUrl: { | ||
| android: "https://play.google.com/store/apps/details?id=net.bawes.custom", | ||
| ios: "https://apps.apple.com/app/id1234567890", | ||
| }, | ||
| }); | ||
| }); | ||
|
|
||
| it("keeps minimum and latest native versions distinct", async () => { | ||
| process.env.MOBILE_MIN_NATIVE_VERSION = "1.0.0"; | ||
| process.env.MOBILE_LATEST_NATIVE_VERSION = "1.2.0"; | ||
| const VersionController = await loadVersionController(); | ||
| const { routes, app, json, res } = createRouteHarness(); | ||
|
|
||
| new VersionController(app); | ||
| routes.get("/api/version")?.({} as Request, res); | ||
|
|
||
| expect(json.mock.calls[0]?.[0]).toMatchObject({ | ||
| minNativeVersion: "1.0.0", | ||
| latestNativeVersion: "1.2.0", | ||
| }); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| # Dependencies | ||
| node_modules/ | ||
|
|
||
| # Capacitor platform folders — added by separate PRs | ||
| # android/ is added by issue #4 | ||
| # ios/ is added by issue #5 | ||
| android/ | ||
| ios/ | ||
|
|
||
| # Fastlane build outputs | ||
| fastlane/report.xml | ||
| fastlane/screenshots | ||
| fastlane/test_output | ||
|
|
||
| # Signing — NEVER commit real keystores or certificates | ||
| *.keystore | ||
| *.jks | ||
| google-play-key.json | ||
| certs/ | ||
| *.p12 | ||
| *.mobileprovision | ||
|
|
||
| # Bundler | ||
| .bundle/ | ||
| vendor/ | ||
|
|
||
| # Build artifacts | ||
| dist/ | ||
| *.apk | ||
| *.aab | ||
| *.ipa |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| source 'https://rubygems.org' | ||
|
|
||
| # Fastlane — used by both Android (issue #4) and iOS (issue #5) lanes | ||
| gem 'fastlane' | ||
|
|
||
| # For automated version and build number bumping | ||
| gem 'fastlane-plugin-versioning' |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.