app-builder
A small, reusable build kit that turns a static web app into two double-clickable, fully offline deliverables — a single-file portable Windows EXE and an Android APK.
It is a kit, not an app. App repos consume it through one workflow file; the kit builds the artefacts and publishes them to a GitHub Release.
The build kit behind the EXE and APK releases
app-builder is the shared kit that produces the portable Windows EXE and Android APK releases of drsamonline app repos. An app repo drops in one workflow file, points it at the folder holding its index.html, and every push to main yields two fresh artifacts attached to a GitHub Release.
Static in, native out
The app itself stays a single self-contained HTML file. The kit wraps that file in a native shell — it never becomes a new codebase.
No backend, no network
Both deliverables run entirely offline. The EXE serves the app from a loopback port; the APK loads it from bundled assets.
One reusable workflow
Consuming repos check out drsamonline/app-builder in their workflow. Improvements to the kit reach every app at once.
One HTML file → two offline deliverables
Each job is independent, so a failure in one build does not block the other.
Portable Windows EXE
Built with PyInstaller --onefile from exe/build.py. It bundles a tiny Python launcher that serves the app folder with http.server on a free 127.0.0.1 port, opens the default browser, and stays running until the console window is closed.
- build
- PyInstaller, single file
- serves
- bundled
app/on loopback - runtime
- no install, fully offline
Android APK
Built from a deliberately minimal, dependency-free WebView Gradle project — the platform framework only, no AndroidX. A single full-screen WebView loads file:///android_asset/www/index.html; the hardware back button navigates history first.
- sdk
- compileSdk 34 · minSdk 24 · targetSdk 34
- plugin
- AGP 8.x
- signing
- debug key by default (see below)
Read this before you ship an APK
Sideload-grade — not Play-Store signing
The kit ships keystore/debug.keystore, a debug-grade key (alias androiddebugkey, store & key password android, CN=Android Debug, ~10 000-day validity).
- Its only job is so successive builds can be installed over each other while sideloading and iterating.
- It is not a Play-Store signing key. Do not publish to Google Play with it.
- It is public in the repository, so it provides no security whatsoever.
To sign with your own key, add repository secrets — KEYSTORE_FILE, KEYSTORE_PASSWORD, KEY_ALIAS, KEY_PASSWORD. app/build.gradle.kts reads those environment variables and falls back to the committed debug key when they are unset.
Permanent links that track the latest build
The release job always uses the fixed tag app-latest and recreates it on every run, so /releases/latest always resolves to the newest build. Point a landing page at these URLs and the download never goes stale.
https://github.com/drsamonline/<repo>/releases/latest/download/<repo>-portable.exe https://github.com/drsamonline/<repo>/releases/latest/download/<repo>.apk
The asset name follows the repo's APP_NAME, so <repo> is that same value.
One file, two inputs
Copy examples/build-apps.yml into your app repo as .github/workflows/build-apps.yml and set the two values at the top to match your layout.
env: APP_NAME: my-app # -> my-app-portable.exe and my-app.apk APP_SRC: docs/play # folder holding index.html (+ any assets)
- exe job —
windows-latest.Checks out your repo and this kit, copies
APP_SRCintoexe/app/, runspython exe/build.py, and uploads<APP_NAME>-portable.exe. - apk job —
ubuntu-latest.Installs the Android SDK packages, copies
APP_SRCintoapk/app/src/main/assets/www/, and runsgradle assembleReleaseto produce a signed<APP_NAME>.apk. - release job.
Downloads both artifacts and publishes them on a single fixed-tag release (
app-latest), deleting any previous one first so/releases/latestresolves to the newest build.