GitHub Actions pipeline that builds patched Android APKs with Morphe patches, morphe-desktop, and APKEditor. Outputs are signed, per-app versioned, and ready for Obtainium.
Forking for personal use is encouraged. To add or remove an app, edit config.json patch_repos — no workflow changes needed.
Important
Legal and security notice. This project downloads and modifies third-party Android applications (via APKMirror, APKPure, and upstream patch repositories). You are responsible for ensuring that your use of the resulting patched APKs complies with each app's license, terms of service, and the laws of your jurisdiction. The maintainers do not host, mirror, or redistribute any copyrighted APK — every APK is fetched at build time from the upstream source you configure.
The build signs patched APKs with your keystore (KEYSTORE_BASE64 / KEYSTORE_PASSWORD / KEY_ALIAS / KEY_PASSWORD). Treat those secrets as production credentials: do not paste them into issues or PRs, do not reuse a personal keystore across experiments, and review the upstream patch repo before trusting it with your signing material. See SECURITY.md for the disclosure policy and how to report a vulnerability privately.
| App | Package | Patch repo |
|---|---|---|
| YouTube | com.google.android.youtube |
MorpheApp/morphe-patches |
| YouTube Music | com.google.android.apps.youtube.music |
MorpheApp/morphe-patches |
com.reddit.frontpage |
MorpheApp/morphe-patches |
|
| Sofascore | com.sofascore.results |
hoo-dles/morphe-patches |
| Twitch | tv.twitch.android.app |
RookieEnough/De-Vanced |
| NZB360 | com.kevinforeman.nzb360 |
rushiranpise/morphe-patches |
| AIDA64 | com.finalwire.aida64 |
rushiranpise/morphe-patches |
| Mimo | com.getmimo |
hoo-dles/morphe-patches |
| Flightradar24 | com.flightradar24free |
rushiranpise/morphe-patches |
| Workflow | File | Trigger | Purpose |
|---|---|---|---|
| Build Morphe-patched apps | morphe-build.yml |
daily 05:15 UTC + manual | Main pipeline. check-versions resolves latest tags and drops apps whose release already exists → build (matrix per app) downloads the APK, applies patches, signs → create-release publishes one GitHub Release per app and prunes the oldest (keeps 2 per app). |
| Update patches.json from upstream repos | update-patches.yml |
manual + push to config.json |
Refreshes patches.json from each upstream patch repo. New patches default to true; existing toggles are preserved. |
| CI | ci.yml |
PR + push to main |
npm ci → eslint → jest. Gates broken PRs. |
| CodeQL | codeql.yml |
PR + push to main + daily 06:00 UTC |
Static security analysis (JavaScript/TypeScript + Actions). |
Each app gets its own release with tag <app>-v<base-version>-<patches-version>:
youtube-v20.44.38-v1.24.0-dev.8.apk
ytmusic-v8.44.54-v1.24.0-dev.8.apk
sofascore-v26.07.27-v1.0.0.apk
Create one Obtainium entry per app against this repo with a Release Tag Filter matching name:
Signed builds are enforced — missing KEYSTORE_BASE64 or KEYSTORE_PASSWORD = build fails.
| Secret | Required | Description |
|---|---|---|
KEYSTORE_BASE64 |
yes | Base64 of your keystore |
KEYSTORE_PASSWORD |
yes | Keystore password |
KEY_ALIAS |
no | Defaults to first alias in keystore |
KEY_PASSWORD |
no | Only if key password ≠ keystore password |
APKMIRROR_API_USER |
no | APKMirror-API username (skips Playwright fallback) |
APKMIRROR_API_PASS |
no | APKMirror-API password |
Multi-source fallback (first valid result wins): pre-downloaded tools/*.apk → URL cache (~/.cache/auto-morphe-builder/urls/) → config.json download_urls → parallel resolution via apkeep (APKPure), APKMirror-API (if creds set), APKMirror scraper (curl → Chromium fallback for /all-versions/ slug when Cloudflare blocks).
Split packages (XAPK/APKM/APKS) are saved as .apk on disk and detected by content, not extension — aapt validation is skipped on the outer zip-of-zips, the inner base.apk is validated post-merge. Sofascore's 57MB arm64-v8a XAPK wins over the 93MB universal by size.
- Architecture:
preferred_archfromconfig.json(defaultarm64-v8a) - DPI preference (APKMirror only):
nodpi→120-640dpi→480-640dpi→120-480dpi→240-480dpi - Split packages: APKEditor merge → fallback to dex-bearing APK extraction
- Rejects dex-less APKs (must contain
classes*.dex)
- Decode
KEYSTORE_BASE64→tools/source.keystore(no type detection or conversion step) - Pass the keystore straight to
morphe-desktop patch --keystorealong with--keystore-password,--keystore-entry-password(defaults to--keystore-passwordifKEY_PASSWORDis unset), and--keystore-entry-alias(auto-detected viakeytool -listifKEY_ALIASis unset). morphe-desktop auto-detects PKCS12 / JKS / BKS from file contents and converts to BKS internally - morphe-desktop signs the patched APK in place
- Fail immediately on any signing error (no
--unsignedfallback)
Chosen APK has no classes.dex— split/config APK picked. The post-merge step re-validatesbase.apk's versionName for XAPK/APKM/APKS bundles.[pkg] could not determine version—morphe-desktop list-versionsreturned no matching version. Verifypatch_repos[*].repoactually publishes a.mppfor your app.Wrong version of key store— wrongKEYSTORE_PASSWORD, orKEY_PASSWORDdiffers from keystore password.- Arm64-v8a APK arrives with armeabi-v7a libs only — upstream bundle is mislabeled. Use
pin_versionto lock to a known-good APK version. - Obtainium not finding updates — verify the Release Tag Filter regex matches the
namefield inconfig.json.
For the full catalogue of symptoms, causes, and fixes see docs/troubleshooting.md.
docs/configuration.md— everyconfig.jsonfield, with type, required-ness, example, and validation rules.docs/architecture.md— pipeline architecture, thecheck-versions→build→create-releasejob graph, and the signing model.docs/troubleshooting.md— symptom / cause / fix catalogue for every common failure mode.docs/release-process.md— release tag format, per-app isolation, pruning (KEEP_COUNT), and rollback procedure.SETUP.md— step-by-step fork + signing keystore + GitHub Actions secrets walkthrough.CONTRIBUTING.md— how to file issues, send PRs, and validate locally before pushing.SECURITY.md— vulnerability disclosure policy and how to report a private issue.
Licensed under the GNU General Public License v3.0. See LICENSE.

