Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

136 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Morphe Builder logo

Morphe Builder

Verified Morphe-patched Android builds from stock Google Play splits, configured in one TOML file.

Documentation | GitHub Template guide | Releases

Daily verified update Python Java uv


Morphe Builder downloads stock APK splits from Google Play, verifies their integrity and signing certificate, merges them, patches them with the Morphe CLI, signs the result, and packages non-root APKs plus deterministic Magisk/KernelSU modules.

Every app, architecture, patch source, and tool version is declared in a single TOML file, so a fork can build its own release matrix in CI without touching Python code. Nothing untrusted is published: downloads stay in a temporary directory until manifest coverage, hashes, package identity, version, ABI, and signer all pass, and each published set carries deterministic provenance.

Google Play delivery uses pinned goopdl releases from PyPI, with verified direct, archive, APKMirror, and Uptodown fallbacks.

Requirements

Requirement Version Notes
Python 3.11–3.13 3.11 minimum for standard-library tomllib
Java 21+ Required by Morphe CLI, APKEditor, and uber-apk-signer
uv latest Only for development installs from source

Windows and Linux are supported. See docs/security.md for platform limits.

Features

  • Verified downloads: Google manifest coverage, safe paths, sizes, SHA-256 hashes, package identity, version, split requirements, ABI, and signing certificate are all checked before publication.
  • Signer pinning: YouTube and YouTube Music require known Google signers; any app can pin its own expected-signer SHA-256.
  • Provider fallback: fixed order google-play β†’ direct β†’ archive β†’ apkmirror β†’ uptodown; integrity failures stay terminal instead of falling through to a later provider.
  • Config-driven matrix: apps, architectures, densities, patch sets, and module output live in TOML; no hardcoded app registry.
  • Per-app toolchains: each app can override Morphe CLI version, patch source, and patch version, resolved and cached per owner/repo@tag.
  • Pinned, hashed tooling: Morphe CLI, patches, APKEditor, and uber-apk-signer are resolved to exact tags with recorded asset hashes in toolchain-provenance.json.
  • Non-root APKs and modules: deterministic Magisk/KernelSU modules that install matching stock, survive reboots, and receive updates from the Magisk app.
  • Reproducible provenance: provenance.json, build-summary.json, release-manifest.json, and CHANGELOG.md record every source, version, and hash.
  • Atomic publication: existing output is never overwritten; a failed job removes its temporary set and publishes nothing.
  • Verified CI releases: daily update checks resolve upstream versions, skip unchanged builds, verify every artifact, and attach GitHub artifact attestations.
  • Secret-safe by design: no committed credentials, no cached tokens or APK payloads, redacted error output.

Build without installing

Use this repository as GitHub Template or fork, edit morphe.toml, and run Manual verified build from Actions. GitHub runner installs toolchain, builds verified artifacts, and uploads them without requiring Python, Java, uv, pipx, or Morphe Builder on your computer.

Start here: docs/template-usage.md.

Installation

Local installation is optional. Morphe Builder depends on reviewed, exactly pinned goopdl releases from PyPI.

Global tool install:

uv tool install morphe-builder

Into an existing environment:

python -m pip install morphe-builder

Development install from source:

git clone https://github.com/Villoh/morphe-builder
cd morphe-builder
uv sync
uv run morphe-builder --help

Quick start

morphe-builder auth --arch arm64
morphe-builder tools
MORPHE_KEYSTORE_PASSWORD=secret morphe-builder build morphe.toml \
  --keystore /path/outside/repository/builder.p12 \
  --output build

auth stores Google Play credentials outside the repository, tools fetches and hashes the pinned Java toolchain, and build produces the full configured matrix from morphe.toml.

Google Play access

Reliable direct downloads use two environment variables together:

Variable Value
GOOPDL_ACCOUNT_EMAIL Dedicated Google account email.
GOOPDL_AAS_TOKEN Persistent AAS token starting with aas_et/.

A partial configuration fails closed. Without either value, goopdl uses the anonymous dispenser and the builder continues through configured verified fallbacks when Play is unavailable.

Generate a token through an isolated browser with morphe-builder aastoken --browser. No-computer option: goopdl-auth helps create credentials without running Morphe Builder, goopdl, or Termux locally. Full local and CI setup: docs/authentication.md. Treat an AAS token as a password: never place it in TOML, argv, committed files, caches, artifacts, or logs.

Other environment variables

Variable Used by Purpose
MORPHE_KEYSTORE_PASSWORD build Signing keystore password. Required; never accepted in argv.
GOOPDL_BROWSER aastoken --browser Browser executable when automatic detection fails.
MORPHE_DISPENSER_URL auth, download Custom anonymous dispenser, used only when no Google account is configured.
MORPHE_MODULE_VERSION_CODE build Monotonic module versionCode. CI passes the workflow run number.
GITHUB_TOKEN tools, build, list-patches Authenticates GitHub API calls. Never sent to asset downloads.
HTTP_PROXY, HTTPS_PROXY build Proxy for provider downloads.
XDG_CACHE_HOME, LOCALAPPDATA all cached commands Override the default cache root.

Configuration

Everything a fork needs to change lives in morphe.toml. No Python code has to be touched to build a different set of apps.

The file has one [toolchain] table with the default Morphe CLI and patch sources, then one ordered [[apps]] entry per app:

[toolchain]
morphe-source = "MorpheApp/morphe-desktop"
morphe-version = "latest"
patches-source = "MorpheApp/morphe-patches"
patches-version = "latest"
# Both default to false. Enable per app when needed instead.
include-universal-patches = false
include-experimental-versions = false

[[apps]]
package = "com.google.android.youtube"
name = "YouTube"
slug = "youtube"
patched-package = "app.morphe.android.youtube"
version = "auto"
build-mode = "apk"
arch = "arm64-v8a"
density = "xxhdpi"
include-patches = ["GmsCore support", "SponsorBlock"]
exclude-patches = []

[apps.patch-options."SponsorBlock"]
toast-on-connection-error = true

[apps.google-play]
profile = "D2"
country = "US"

[apps.fallbacks]
apkmirror = ["https://www.apkmirror.com/apk/google-inc/youtube/"]

Only package and name are required. Everything else has a documented default:

Setting Required Default when omitted
package yes Validated Android package name.
name yes Display name used in release notes and modules.
slug no package with dots replaced by hyphens. Controls output, cache, and module names; must be unique.
patched-package no No package change is expected after patching. Set it when patches rename the package.
version no auto: highest version supported by every selected patch. Also accepts latest or an exact version name.
version-code no Resolved from the version name. Requires an explicit version; auto and latest are rejected with it.
build-mode no apk: non-root APK only. Use module or both for Magisk/KernelSU modules.
arch no arm64-v8a. Also arm-v7a, all, or both (two independent jobs).
density no The profile's only available density split is selected automatically.
include-patches no Empty: the patch bundle's default selection is used.
exclude-patches no Empty: nothing is removed from the selection.
exclusive-patches no Unset. When set, only these patches apply; cannot be combined with include/exclude.
patch-options no Patch defaults. Scalar values only, never shell-interpolated. Every option must belong to a selected patch.
expected-signer no Unpinned: a valid provider-served signer is accepted and logged as unpinned signer accepted. YouTube and YouTube Music always enforce built-in Google signer pins.
include-universal-patches no Inherits [toolchain] (false). Include Universal patches, such as Change package name, for this app.
include-experimental-versions no Inherits [toolchain] (false). Include experimental versions when resolving this app.
expect-corrupted-version-code no false. Explicit workaround for upstream patched-manifest bug: expect output versionCode 2147483647, not stock code.
google-play.profile no goopdl picks a profile. Leave unset with arch = "both", since one profile cannot cover both ABIs.
google-play.country no goopdl account region.
google-play.proxy no Direct connection.
google-play.dispenser no MORPHE_DISPENSER_URL, or the built-in public dispenser.
fallbacks.* no Empty: Google Play is the only source, so a Play failure fails the job.
patches-source, patches-version, cli-source, morphe-version no The [toolchain] values.

include-universal-patches and include-experimental-versions may instead be set under [toolchain] to establish defaults for every app. Both default to false; app-level values override them. Universal patches do not constrain compatible-version selection. A selection containing only Universal patches fails because it cannot establish a source version.

expect-corrupted-version-code is intentionally narrow. Current MorpheApp/morphe-patches changes patched versionCode to 2147483647 while recompiling affected manifests. Enable it only for an affected app; provenance retains the real downloaded version code. It applies to both non-root APKs and root module payloads. Root modules always disable Change package name because they must retain stock package identity.

The [toolchain] table itself is optional; omitting it uses MorpheApp/morphe-desktop and MorpheApp/morphe-patches at latest.

Parsing is strict: unknown keys, duplicate packages, duplicate slugs, unsupported values, and conflicting patch selections are all rejected before any download starts. Run morphe-builder list-patches morphe.toml to see which version each app will resolve to before committing a change.

For CI-built releases, declare at least one fallbacks entry per app. Anonymous Google Play access is unreliable from hosted runners, and a verified APKMirror or Uptodown source keeps the build working without credentials.

Full field reference, version-resolution order, and fallback URL rules: docs/configuration.md.

Usage

Command Purpose
auth Authenticate against Google Play and cache the session outside the repository.
download Download and verify one package's splits into an output directory.
tools Resolve, download, and hash the pinned Java toolchain.
list-patches Show patch-compatible versions for the configured apps.
build Run the full configured matrix: download, merge, patch, sign, verify, package.
verify Re-verify an existing split set against a package, architecture, and version.
clean Remove the disposable work cache only; trusted sets and tools are preserved.
morphe-builder download com.example.app --arch arm64 --output downloads
morphe-builder list-patches morphe.toml
morphe-builder verify trusted/splits com.google.android.youtube \
  --arch arm64 --version-name 21.04.223

All commands accept --json (progress on stderr, machine-readable result on stdout). --arch both runs independent ARM64 and ARMv7 jobs. Keystore passwords come from MORPHE_KEYSTORE_PASSWORD, never argv. Exit codes are stable per failure category.

Full reference, exit-code table, and cache semantics: docs/cli.md.

Documentation

Topic Document
Installation and first build docs/getting-started.md
GitHub Template/fork without local installation docs/template-usage.md
System architecture and trust boundaries docs/architecture.md
Google Play authentication docs/authentication.md
CLI reference and exit codes docs/cli.md
TOML configuration and version resolution docs/configuration.md
Toolchain cache, pins, and checksums docs/toolchain.md
Google Play delivery and integrity checks docs/google-play-integrity.md
Provider fallback order docs/providers.md
Split selection and merge policy docs/merge.md
Signed APK build flow docs/build.md
Magisk/KernelSU module format docs/modules.md
Threat model and platform limits docs/security.md
CI, release verification, and publication docs/releases.md
Third-party licenses docs/THIRD_PARTY.md
Troubleshooting docs/troubleshooting.md
Contributor guide docs/contributing.md
Repository rules for contributors and agents docs/rules/README.md
Active roadmap docs/roadmap/ROADMAP.md
Completed milestones and evidence docs/roadmap/COMPLETED.md

License

Morphe Builder is released under the MIT License. Downloaded Morphe tooling remains separately licensed under GPL-3.0 and is not bundled in the Python distribution. See docs/THIRD_PARTY.md.

Credits

Releases

Packages

Contributors

Languages