-
released this
2026-02-27 02:41:47 +01:00 | 16 commits to main since this release📅 Originally published on GitHub: Fri, 27 Feb 2026 01:42:45 GMT
🏷️ Git tag created: Fri, 27 Feb 2026 01:41:47 GMTFirst successful APK build confirmed ✓
After deploying to an Intel-based Ubuntu VPS (2 cores, 4 GB RAM), the first complete APK build was produced end-to-end. This release contains the fixes that were required to get there.
What changed
Bug fixes
nginx: dynamic DNS resolution for the backend proxy
Without a
resolverdirective, nginx resolves thebackendcontainer's hostname once at startup and caches the IP indefinitely. When the backend container restarts (e.g. after an OOM kill), it often receives a new internal Docker IP — nginx keeps hitting the old address, returning 502 Bad Gateway for every/api/*request. The frontend's own healthcheck routes through the same proxy, so it also fails and the container is marked unhealthy.Fix:
resolver 127.0.0.11 valid=30s ipv6=off+set $backend_upstream http://backend:3001variable-basedproxy_pass. Nginx now re-resolves the hostname on each request (TTL 30 s) so the proxy recovers automatically after a backend restart without any manual intervention.SSE keep-alive heartbeat
nginx and upstream load-balancers close idle connections after their own read/send timeouts. During a Gradle dependency download on a slow link, the SSE stream can be completely silent for minutes — long enough to be killed. The backend now emits an SSE comment line (
: heartbeat) every 15 seconds, keeping the stream alive for the full build duration.Gradle JVM heap cap (OOM prevention)
On a 4 GB host running Docker, an unconstrained Gradle JVM can exhaust available RAM. The Linux OOM-killer then terminates the Node.js backend process, the container restarts, and the nginx DNS-caching issue above turns a temporary blip into a permanent outage.
GRADLE_OPTS=-Xmx512m -Xms128mis now set by default — sufficient for a standard TWA release build while leaving enough headroom for the host OS and Docker daemon. Override viaGRADLE_OPTSin.envif needed.Observability improvements
HTTP request logging (Morgan)
docker compose logs backendnow shows a timestamped line for every inbound request, making it immediately clear whether requests are reaching the backend at all or being rejected at the nginx proxy level.Process-level error handlers
unhandledRejectionanduncaughtExceptionare now logged to stdout before the process exits, surfacing crash reasons indocker compose logs backendfor post-mortem analysis.
Files changed
File Change frontend/nginx.confAdd resolver 127.0.0.11, variableproxy_pass,proxy_send_timeout 600sbackend/src/routes/build.tsSSE keep-alive heartbeat every 15 s backend/src/index.tsMorgan request logging, unhandledRejection/uncaughtException handlers backend/src/services/builder.tsGRADLE_OPTS=-Xmx512m -Xms128mdefaultbackend/package.jsonAdd morgandependencyREADME.mdDocument GRADLE_OPTS, low-memory note, updated feature liststats.mdSession 4 log, debug entries 13–16, updated cumulative totals
Upgrading
git pull docker compose up -d --buildThe frontend image needs a rebuild (nginx.conf changed). The backend image only needs a
docker compose restart backendif you don't want to wait for a full rebuild, but rebuilding is recommended to pick up the Morgan dependency.Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
Source code (ZIP)
-
v0.0.2-alpha — Builds and runs in production Pre-release
released this
2026-02-26 18:26:25 +01:00 | 25 commits to main since this release📅 Originally published on GitHub: Thu, 26 Feb 2026 17:28:19 GMT
🏷️ Git tag created: Thu, 26 Feb 2026 17:26:25 GMTIt builds. It runs. First production deployment ✅
This release marks the first confirmed working production deployment at pwa.macjuu.com.
What's working
- Full Docker Compose stack builds cleanly end-to-end
- Frontend (Nginx + React SPA) and backend (Express + Android SDK) both start healthy
- APK generation pipeline confirmed operational
- All 83 tests passing (46 backend · 37 frontend)
Changes since v0.0.1-alpha
Bug fixes
@bubblewrap/coreAPI —Logdoes not exist in v1.24.1; replaced withConsoleLog(zero-arg constructor). Updated Vitest mock to match.- TypeScript type error —
Parameters<typeof TwaManifest>failed because TwaManifest has an overloaded constructor; replaced withas anycast. - Docker frontend build —
ManifestDefaultswas imported but unused inBuildForm.tsx; removed to satisfynoUnusedLocals: true.
Deployment
HOST_PORTenv var (default 8088) controls which host port the frontend container binds to — avoids conflicts on homelab servers where ports 80 and 8080–8086 are typically occupied.docker-compose.ymlnow uses${HOST_PORT:-8088}:80instead of the hardcoded80:80.
Known limitations (alpha)
- No built-in authentication — protect at reverse proxy level (basic auth, IP allowlist, OAuth2 proxy)
- Each APK build generates a fresh signing key; upgrading an installed app requires uninstalling first
- Single-instance only (in-memory build store, no persistence across restarts)
Install
git clone https://github.com/niels-emmer/pwa-maker-android.git cd pwa-maker-android cp .env.example .env # edit HOST_PORT if 8088 is taken docker-compose up -d --build # first build: ~20 min; subsequent: fastDownloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
v0.0.1-alpha — Initial release Pre-release
released this
2026-02-26 14:15:33 +01:00 | 34 commits to main since this release📅 Originally published on GitHub: Thu, 26 Feb 2026 13:16:27 GMT
🏷️ Git tag created: Thu, 26 Feb 2026 13:15:33 GMTPWA Maker Android — v0.0.1-alpha
First public release. Self-hosted, Dockerised web app that turns any HTTPS Progressive Web App into a signed Android APK ready to sideload — no Android Studio, no Play Store.
What's included
Core build pipeline
- Accepts any HTTPS PWA URL and fetches the web manifest automatically
- Generates a Trusted Web Activity (TWA) Android project via
@bubblewrap/core - Builds with Gradle + Android SDK 34 (target API 34, min API 21 / Android 5.0)
- Auto-generates a per-build signing keystore (RSA-2048); sideload-only
UI
- Dark-themed, mobile-first React + Tailwind frontend
- Manifest fields auto-filled from URL; all fields remain editable
- Real-time build log streamed via Server-Sent Events
- Download signed APK directly from the browser when the build completes
Deployment
- Single
docker-compose up -d --buildto deploy - Backend image: Node 20 + JDK 17 + Android SDK 34 (~1.5 GB, built on-demand)
- Gradle dependency cache as Docker named volume (fast subsequent builds: ~30s)
- Designed for a VPS behind an SSL-terminating reverse proxy
Security
- Non-root container (
appuserUID 1001) - Zod input validation; all subprocess calls use array args (no shell injection)
- Rate limiting: 3 concurrent builds, 10 builds/hr/IP
- No built-in auth — protect at reverse proxy level (see SECURITY.md)
Tests
- 83 passing: 46 backend (Vitest) + 37 frontend (Vitest + React Testing Library)
Known limitations / alpha caveats
- Each build generates a new signing key. Re-installing a new build requires uninstalling the old APK first (Android enforces signing consistency for upgrades). This is by design for sideload-only use.
- First APK build after a fresh container start takes 2–5 minutes while Gradle downloads dependencies. Subsequent builds use the cached volume (~30s).
- The backend Docker image is ~1.5 GB. Ensure your VPS has sufficient disk space (recommend ≥ 5 GB free).
- No built-in authentication — do not expose to the public internet without a reverse proxy auth layer.
- macOS app generation is not included (simplest functional route was chosen).
Quick start
git clone https://github.com/niels-emmer/pwa-maker-android.git cd pwa-maker-android cp .env.example .env docker-compose up -d --buildThen point your SSL reverse proxy at port 80.
Feedback
This is an alpha release. Open an issue for bugs, unexpected behaviour, or feature requests.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
mirror of
https://github.com/niels-emmer/pwa-maker-android.git
synced 2026-09-13 23:53:33 +02:00