ReVanced-auto builds patched Android APKs from a TOML config: resolve compatible app versions, download stock APKs, apply Morphe CLI + Morphe-compatible patch bundles, re-sign the result, and write artifacts to build/.
- Python 3.15 (
requires-python = ">=3.15,<3.16") - Java 21+, an OpenJDK build such as Temurin (
mise installprovides Temurin 25). Oracle JDK and GraalVM reject the unsigned BouncyCastle provider bundled in the Morphe CLI, so patching fails withJCE cannot authenticate the provider BC/Invalid password for keystore entry, even with correct passwords. uv- Google Chrome, only for apps whose APKMirror pages sit behind a Cloudflare challenge (currently Discord). It runs
headed, so a headless Linux machine needs
xvfb-run. Other apps never start it. - Optional but useful:
zipalign,optipng
This repo uses uv for dependency management and is not distributed as a standalone Python package.
git clone <repo-url>
cd Revanced-auto
mise install
uv sync --locked --all-groups
uv run python -m scripts.cli check-envcheck-env verifies required tools, Java, downloads the required bin/ jars when missing, checks optional helpers and assets, and validates config.toml syntax.
Prefer the Python CLI. After uv sync, use uv run ... for one-off commands, or activate the uv environment first if you are working in an interactive shell session.
# Optional: only needed when you sign with your own keystore (see "Signing and build flow").
# Prefer a secure secret source over typing passwords into shell history.
# export KEYSTORE_PASSWORD='...'
# export KEYSTORE_ENTRY_PASSWORD='...'
uv run python -m scripts.cli check-env
uv run python -m scripts.cli check --config config.toml
uv run python -m scripts.cli build --config config.toml --parallel 1The CLI builds every app with enabled = true in config.toml. To rebuild when nothing changed, reset the version tracker first:
uv run python -m scripts.cli version-tracker reset --config config.tomlUseful variants:
uv run python -m scripts.cli build --config config.toml --parallel 2
uv run python -m scripts.cli build --config config.toml --parallel 1 --clean
uv run python -m scripts.cli build --config config.toml --parallel 1 --no-cache
uv run python -m scripts.cli version-tracker check --config config.toml
uv run python -m scripts.cli cache statsThis repo ships with Morphe as the default patcher (MorpheApp/morphe-patches + MorpheApp/morphe-cli). The sample config.toml enables nine apps (see Configuration).
Setup is the same as above. The keystore passwords default to the tracked assets/ks.keystore's, so they are only
needed for your own keystore.
# Sanity check (does not download APKs)
uv run python -m scripts.cli check --config config.toml
# Build all enabled apps
uv run python -m scripts.cli build --config config.toml --parallel 2To force a specific CLI profile, set cli-profile in config.toml. Valid values:
"auto"(default) — detect from the CLI JAR's--helpoutput"morphe-cli""adobo-cli"(Adobo patches run on Morphe CLI)
- Official Morphe Website
- Morphe Patches site
- Morphe Documentation
- Awesome for Morphe — curated list of Morphe tools, patch indexes, and community resources
- Patch Explorer — browse all Morphe-supported apps and patches
- Morphe Patch Tracker — latest patches release
- Morphe CLI releases — latest CLI release
- CLI detection picks the wrong profile — set
cli-profile = "morphe-cli"inconfig.tomlto force it. patch --helpflags differ from what the docs say — Morphe CLI tracks upstream; pincli-version = "vX.Y.Z"to lock behavior.- Build succeeds but patches are not applied — confirm
patches-sourceresolves to a published release andpatches-versionmatches the CLI's expected bundle shape.
uv run python -m scripts.cli build --config config.toml --parallel N [--clean] [--no-cache]
uv run python -m scripts.cli check --config config.toml
uv run python -m scripts.cli version-tracker {check|save|show|reset} --config config.toml
uv run python -m scripts.cli cache init
uv run python -m scripts.cli cache stats
uv run python -m scripts.cli cache cleanup [--force]
uv run python -m scripts.cli cache clean [--pattern '.*\.apk']
uv run python -m scripts.cli lint [--fix]
uv run python -m pytest tests -vThe shipped config.toml uses top-level global settings plus one [Section] per app. The full key reference is
docs/CONFIG.md. Each enabled app needs at least one download source, tried in this order until one works:
apkmirror-dlurlarchive-dlurlapkpure-dlurlgithub-dlurl(e.g."https://github.com/owner/repo"; end the URL with/<asset>.apkto pin a release asset by name, e.g.".../releases/latest/download/BraveMonoarm64.apk")aptoide-dlurlapkmonk-dlurluptodown-dlurl(currently unusable: the version-list pages the scraper reads return 404)
Global settings:
parallel-jobs(0means 2)patches-version,cli-version(latest,devor a tag)patches-source(defaults toMorpheApp/morphe-patches)cli-source(defaults toMorpheApp/morphe-cli)cli-profile(defaults to"auto"; detectsmorphe-clioradobo-clifrom--help)arch(default architecture, applied to every app that does not set its ownarch)riplibkeystore-path,keystore-alias(defaultassets/ks.keystoreandmorphe)
Per-app settings worth knowing:
app-name,rv-brand— output name and brand label. These are per-app only; global values are ignored.patches— array of patch names to enableexclude-patches— array of patch names to disableexclusive— whentrue, only the names inpatchesare enabled; every other patch stays offpatch-options."Patch Name"— a sub-table ofkey = "value"options passed to a specific patchpackage-name— package id used to look up patch-compatible versions when it cannot be derived from a download URL (for example APKMirror-only apps;Reddit-Morphedusescom.reddit.frontpage)version—auto(default) picks the newest version compatible with the patches,latestthe newest store version,betathe newest including beta/alpha, or pin an exact versionpatches-source,cli-source,arch— override the global values
The sample config enables TikTok, YouTube-Morphed, Music-Morphed, Reddit-Morphed, Brave, SDMaid, SoundCloud, GoogleMaps, and Discord. GooglePhotos, Instagram-Piko, Twitter-Piko and X-Piko are included as disabled examples; flip enabled = true to use them. The Piko sections are disabled because Piko supports only one old app version each that the scrapers cannot reach.
patches-source (and cli-source) accepts a GitHub repo slug like owner/repo. A list of repo slugs is also accepted; later entries override earlier ones when patches conflict. Every bundle runs on the Morphe CLI. docs/PATCHES.md lists the bundles the sample config uses. The common ones:
- Morphe (default) —
MorpheApp/morphe-patches+MorpheApp/morphe-cli. - Adobo —
jkennethcarino/adobo, extra patches on top of Morphe. - Piko (Twitter/X, Instagram) —
crimera/piko. - External bundles —
brosssh/revanced-external-bundlesresolves a patch JAR via the community aggregator's GraphQL API (https://revanced-external-bundles.brosssh.com). Useexternal-bundles:<bundle_type>to pin a specific bundle type; the bare repo slug falls back to selecting by the app's package id.
Config docs and generator:
docs/CONFIG.md— config referencedocs/README.md— docs indexdocs/index.html— local docs landing pagedocs/generator.html— local config generatorCHANGELOG.md— changelog
Plugins in scripts/plugins/ are auto-discovered. Each plugin module must expose:
def handle_hook(ctx, stage: str) -> None: ...Stages: pre_pipeline, post_pipeline.
- The keystore is
keystore-pathfromconfig.toml, orassets/ks.keystorewhen unset; the alias iskeystore-alias(defaultmorphe). KEYSTORE_PASSWORDdefaults toMorphe, which opens the trackedassets/ks.keystore.KEYSTORE_ENTRY_PASSWORDdefaults to the keystore password. Set both for your own keystore (CI passes them from secrets).- Stock APK signature checks use
assets/sig.txtwhen entries are available. - Final APKs are re-signed with
bin/apksigner.jarusing v1 + v2 only; v3/v4 are intentionally disabled for compatibility and predictable signing behavior. The jar is downloaded on demand when missing. - Keystores are auto-converted to BKS (cached under
.cache/keystore/) before patching, since Morphe's patcher only accepts BKS; a PKCS12/JKS keystore such asassets/ks.keystoreconverts transparently on first use.
Normal flow:
- Load
config.toml. - Compare current config state with
.github/last_built_versions.json. - Skip the build if nothing tracked changed.
- If
--cleanis set, removetemp/,build/,logs/, andbuild.md. - Resolve compatible versions (
version = "auto"), download stock APKs, and fetch CLI/patch prebuilts. - Patch, sign, and write final artifacts to
build/. - Save the new version state after a successful build.
For YouTube and YouTube Music builds, expect to install MicroG-RE on-device. The release notes link it once; you can also get it directly from https://github.com/morpheapp/microg-RE/releases/latest.
build/— final APKs and per-app Markdown summariestemp/— temporary files and the default directory of thecachesubcommands.cache/— downloaded CLI/patch jars (.cache/prebuilts/) and converted keystores (.cache/keystore/).github/last_built_versions.json— saved version-tracker statebuild.md— combined build notes/changelog
The cache subcommands use temp/ by default with a TTL of 86400 seconds (24 hours). Expired entries are reused only until cleanup or replacement. Set CACHE_DIR to move the cache; it applies to both locations above.
- Run
uv run python -m scripts.cli check-envfirst. - If
python -m scripts.cli ...cannot import dependencies, run it throughuv runor activate theuvenvironment first. - If version checks keep skipping builds, inspect or reset
.github/last_built_versions.jsonwithuv run python -m scripts.cli version-tracker show --config config.tomlor... reset --config config.toml. - If downloads fail, set
GITHUB_TOKEN(GitHub API rate limits) and check that the app's other sources work. - If APKMirror answers
403/ "Just a moment...", the page is behind a Cloudflare challenge. The scraper then needs Chrome (see Requirements); the final file download is plain HTTP. - If signing fails, check the Java build first (Oracle JDK/GraalVM cause
Invalid password for keystore entryeven with the right passwords), then the keystore env vars andkeystore-path. DEBUG=1is useful for Python CLI debugging.
Environment variables the scripts read:
KEYSTORE_PASSWORDKEYSTORE_ENTRY_PASSWORDGITHUB_TOKENCACHE_DIRDEBUG
docs/CONFIG.mddocs/README.mddocs/PATCHES.md— curated patch sources and Morphe indexesAGENTS.md
- Morphe — patches and CLI that drive this repo's default build.
- Morphe Patches — index of Morphe-supported apps and patches.
- awesome-for-morphe — curated list of Morphe tools, patch indexes, and community resources.
- builder-for-morphe — minimal Python rewrite based on j-hc/uni-apks, for users who want a simpler single-purpose builder.
- ReVanced — upstream ReVanced project.
- ReVanced Extended (RVX) — community patch fork.
- revanced-external-bundles — community patches aggregator (used by
patches-source = "brosssh/revanced-external-bundles").