From 5eaad33dc1fbd24c36e1498f04e21d068e85f53e Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Mon, 24 Jul 2023 14:18:15 +0200 Subject: [PATCH 01/62] fix: print original instead of kebab cased names --- .../kotlin/app/revanced/utils/patcher/Patcher.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/app/revanced/utils/patcher/Patcher.kt b/src/main/kotlin/app/revanced/utils/patcher/Patcher.kt index 53eb07e..fd61bae 100644 --- a/src/main/kotlin/app/revanced/utils/patcher/Patcher.kt +++ b/src/main/kotlin/app/revanced/utils/patcher/Patcher.kt @@ -17,12 +17,11 @@ fun Patcher.addPatchesFiltered(allPatches: PatchList) { val includedPatches = mutableListOf>>() allPatches.forEach patchLoop@{ patch -> val compatiblePackages = patch.compatiblePackages - val patchName = patch.patchName.lowercase().replace(" ", "-") val args = args.patchArgs?.patchingArgs!! - val prefix = "Skipping $patchName" + val prefix = "Skipping ${patch.patchName}" - if (compatiblePackages == null) logger.trace("$patchName: No constraint on packages.") + if (compatiblePackages == null) logger.trace("${patch.patchName}: No package constraints.") else { if (!compatiblePackages.any { it.name == packageName }) { logger.trace("$prefix: Incompatible with $packageName. This patch is only compatible with ${ @@ -42,15 +41,16 @@ fun Patcher.addPatchesFiltered(allPatches: PatchList) { } } - if (args.excludedPatches.contains(patchName)) { + val kebabCasedPatchName = patch.patchName.lowercase().replace(" ", "-") + if (args.excludedPatches.contains(kebabCasedPatchName)) { logger.info("$prefix: Manually excluded") return@patchLoop - } else if ((!patch.include || args.exclusive) && !args.includedPatches.contains(patchName)) { + } else if ((!patch.include || args.exclusive) && !args.includedPatches.contains(kebabCasedPatchName)) { logger.info("$prefix: Excluded by default") return@patchLoop } - logger.trace("Adding $patchName") + logger.trace("Adding ${patch.patchName}") includedPatches.add(patch) } From 00410d54c006333dd0591063a5f5c57651f5f619 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Mon, 24 Jul 2023 12:20:19 +0000 Subject: [PATCH 02/62] chore(release): 2.22.1-dev.1 [skip ci] ## [2.22.1-dev.1](https://github.com/ReVanced/revanced-cli/compare/v2.22.0...v2.22.1-dev.1) (2023-07-24) ### Bug Fixes * print original instead of kebab cased names ([5eaad33](https://github.com/ReVanced/revanced-cli/commit/5eaad33dc1fbd24c36e1498f04e21d068e85f53e)) --- CHANGELOG.md | 7 +++++++ gradle.properties | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c85dad1..60e857d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [2.22.1-dev.1](https://github.com/ReVanced/revanced-cli/compare/v2.22.0...v2.22.1-dev.1) (2023-07-24) + + +### Bug Fixes + +* print original instead of kebab cased names ([5eaad33](https://github.com/ReVanced/revanced-cli/commit/5eaad33dc1fbd24c36e1498f04e21d068e85f53e)) + # [2.22.0](https://github.com/revanced/revanced-cli/compare/v2.21.5...v2.22.0) (2023-07-11) diff --git a/gradle.properties b/gradle.properties index b61decf..c6b9bfb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true kotlin.code.style = official -version = 2.22.0 +version = 2.22.1-dev.1 From 83f99e29ec306b576730ee261656b6a2e54625b5 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Sun, 30 Jul 2023 02:12:19 +0200 Subject: [PATCH 03/62] ci: Change bumping commit scope --- .releaserc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.releaserc b/.releaserc index 8c4d33c..85c3f70 100644 --- a/.releaserc +++ b/.releaserc @@ -10,7 +10,7 @@ [ "@semantic-release/commit-analyzer", { "releaseRules": [ - { "type": "build", "scope": "revanced-patcher", "release": "patch" } + { "type": "build", "scope": "Needs bump", "release": "patch" } ] } ], From 0a758d86df12c7cdbeb3b140fed7c61782f64cb7 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Sun, 30 Jul 2023 02:14:10 +0200 Subject: [PATCH 04/62] build(Needs bump): Bump dependencies --- build.gradle.kts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 54ebe59..10131c2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -23,14 +23,14 @@ repositories { } dependencies { - implementation("org.jetbrains.kotlin:kotlin-reflect:1.8.20-RC") + implementation("org.jetbrains.kotlin:kotlin-reflect:1.8.20") - implementation("app.revanced:revanced-patcher:11.0.3") - implementation("info.picocli:picocli:4.7.1") + implementation("app.revanced:revanced-patcher:12.0.0") + implementation("info.picocli:picocli:4.7.3") implementation("com.github.revanced:jadb:2531a28109") // updated fork - implementation("com.android.tools.build:apksig:8.1.0-alpha09") + implementation("com.android.tools.build:apksig:8.1.0") implementation("org.bouncycastle:bcpkix-jdk15on:1.70") - implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.14.+") + implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.14.3") testImplementation("org.jetbrains.kotlin:kotlin-test:1.8.20-RC") } From f9cf7d21b7f1c2f11234d604a1047b9d2b165f83 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Sun, 30 Jul 2023 02:46:50 +0200 Subject: [PATCH 05/62] feat: Improve command line argument descriptions --- .../app/revanced/cli/command/MainCommand.kt | 53 +++++++++++-------- src/main/kotlin/app/revanced/utils/adb/Adb.kt | 6 +-- 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/src/main/kotlin/app/revanced/cli/command/MainCommand.kt b/src/main/kotlin/app/revanced/cli/command/MainCommand.kt index 1da0bfd..0a751d7 100644 --- a/src/main/kotlin/app/revanced/cli/command/MainCommand.kt +++ b/src/main/kotlin/app/revanced/cli/command/MainCommand.kt @@ -32,7 +32,7 @@ private class CLIVersionProvider : IVersionProvider { } @Command( - name = "ReVanced-CLI", + name = "ReVanced CLI", mixinStandardHelpOptions = true, versionProvider = CLIVersionProvider::class ) @@ -44,15 +44,15 @@ internal object MainCommand : Runnable { class Args { // TODO: Move this so it is not required when listing patches - @Option(names = ["-a", "--apk"], description = ["Input APK file to be patched"], required = true) + @Option(names = ["-a", "--apk"], description = ["APK file to be patched"], required = true) lateinit var inputFile: File - @Option(names = ["--uninstall"], description = ["Uninstall the mount variant"]) - var uninstall: Boolean = false + @Option(names = ["--unmount"], description = ["Unmount a patched APK file"]) + var unmount: Boolean = false @Option( - names = ["-d", "--deploy-on"], - description = ["If specified, deploy to device over ADB with given name"] + names = ["-d", "--deploy"], + description = ["Deploy to the specified device that is connected via ADB"] ) var deploy: String? = null @@ -78,63 +78,72 @@ internal object MainCommand : Runnable { @Option(names = ["-l", "--list"], description = ["List patches"], required = true) var listOnly: Boolean = false - @Option(names = ["--with-versions"], description = ["List patches with compatible versions"]) + @Option(names = ["--with-versions"], description = ["List patches with version compatibilities"]) var withVersions: Boolean = false - @Option(names = ["--with-packages"], description = ["List patches with compatible packages"]) + @Option(names = ["--with-packages"], description = ["List patches with package compatibilities"]) var withPackages: Boolean = false } class PatchingArgs { - @Option(names = ["-o", "--out"], description = ["Output file path"], required = true) + @Option(names = ["-o", "--out"], description = ["Path to save the patched APK file to"], required = true) lateinit var outputPath: String - @Option(names = ["-e", "--exclude"], description = ["Explicitly exclude patches"]) + @Option(names = ["-e", "--exclude"], description = ["Exclude patches"]) var excludedPatches = arrayOf() @Option( names = ["--exclusive"], - description = ["Only installs the patches you include, not including any patch by default"] + description = ["Only include patches that were explicitly specified to be included"] ) var exclusive = false @Option(names = ["-i", "--include"], description = ["Include patches"]) var includedPatches = arrayOf() - @Option(names = ["--experimental"], description = ["Disable patch version compatibility patch"]) + @Option(names = ["--experimental"], description = ["Ignore patches incompatibility to versions"]) var experimental: Boolean = false - @Option(names = ["-m", "--merge"], description = ["One or more dex file containers to merge"]) + @Option(names = ["-m", "--merge"], description = ["One or more DEX files or containers to merge into the APK"]) var mergeFiles = listOf() - @Option(names = ["--mount"], description = ["If specified, instead of installing, mount"]) + @Option( + names = ["--mount"], + description = ["Mount the patched APK file over the original file instead of installing it"] + ) var mount: Boolean = false - @Option(names = ["--cn"], description = ["Overwrite the default CN for the signed file"]) + @Option(names = ["--cn"], description = ["The common name of the signer of the patched APK file"]) var cn = "ReVanced" - @Option(names = ["--keystore"], description = ["File path to your keystore"]) + @Option(names = ["--keystore"], description = ["Path to the keystore to sign the patched APK file with"]) var keystorePath: String? = null - @Option(names = ["-p", "--password"], description = ["Overwrite the default password for the signed file"]) + @Option( + names = ["-p", "--password"], + description = ["The password of the keystore to sign the patched APK file with"] + ) var password = "ReVanced" - @Option(names = ["-t", "--temp-dir"], description = ["Temporary resource cache directory"]) + @Option(names = ["-t", "--temp-dir"], description = ["Path to temporary resource cache directory"]) var cacheDirectory = "revanced-cache" @Option( names = ["-c", "--clean"], - description = ["Clean the temporary resource cache directory. This will be done anyways when running the patcher"] + description = ["Clean up the temporary resource cache directory after patching"] ) var clean: Boolean = false - @Option(names = ["--custom-aapt2-binary"], description = ["Path to custom aapt2 binary"]) + @Option( + names = ["--custom-aapt2-binary"], + description = ["Path to custom AAPT binary to compile resources with"] + ) var aaptPath: String = "" } override fun run() { if (args.patchArgs?.listingArgs?.listOnly == true) return printListOfPatches() - if (args.uninstall) return uninstall() + if (args.unmount) return unmount() val pArgs = this.args.patchArgs?.patchingArgs ?: return val outputFile = File(pArgs.outputPath) // the file to write to @@ -215,7 +224,7 @@ internal object MainCommand : Runnable { logger.info(result) } - private fun uninstall() { + private fun unmount() { val adb: Adb? = args.deploy?.let { Adb( File("placeholder_file"), diff --git a/src/main/kotlin/app/revanced/utils/adb/Adb.kt b/src/main/kotlin/app/revanced/utils/adb/Adb.kt index 46d1edb..3c51566 100644 --- a/src/main/kotlin/app/revanced/utils/adb/Adb.kt +++ b/src/main/kotlin/app/revanced/utils/adb/Adb.kt @@ -11,7 +11,7 @@ internal class Adb( private val file: File, private val packageName: String, deviceName: String, - private val modeInstall: Boolean = false, + private val install: Boolean = false, private val logging: Boolean = true ) { private val device: JadbDevice @@ -20,7 +20,7 @@ internal class Adb( device = JadbConnection().devices.let { device -> device.find { it.serial == deviceName } ?: device.first() } ?: throw IllegalArgumentException("No such device with name $deviceName") - if (!modeInstall && device.run("su -h", false) != 0) + if (!install && device.run("su -h", false) != 0) throw IllegalArgumentException("Root required on $deviceName. Task failed") } @@ -29,7 +29,7 @@ internal class Adb( } internal fun deploy() { - if (modeInstall) { + if (install) { logger.info("Installing without mounting") PackageManager(device).install(file) From 4105b638c03690cde8b7d975e995ca61f1de6888 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Sun, 30 Jul 2023 00:52:33 +0000 Subject: [PATCH 06/62] chore(release): 2.23.0-dev.1 [skip ci] # [2.23.0-dev.1](https://github.com/ReVanced/revanced-cli/compare/v2.22.1-dev.1...v2.23.0-dev.1) (2023-07-30) ### Features * Improve command line argument descriptions ([f9cf7d2](https://github.com/ReVanced/revanced-cli/commit/f9cf7d21b7f1c2f11234d604a1047b9d2b165f83)) --- CHANGELOG.md | 7 +++++++ gradle.properties | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60e857d..af34b4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [2.23.0-dev.1](https://github.com/ReVanced/revanced-cli/compare/v2.22.1-dev.1...v2.23.0-dev.1) (2023-07-30) + + +### Features + +* Improve command line argument descriptions ([f9cf7d2](https://github.com/ReVanced/revanced-cli/commit/f9cf7d21b7f1c2f11234d604a1047b9d2b165f83)) + ## [2.22.1-dev.1](https://github.com/ReVanced/revanced-cli/compare/v2.22.0...v2.22.1-dev.1) (2023-07-24) diff --git a/gradle.properties b/gradle.properties index c6b9bfb..ec6ceff 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true kotlin.code.style = official -version = 2.22.1-dev.1 +version = 2.23.0-dev.1 From 83b475c2e39441e72571f1b8e20e6b4066f24fd5 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Thu, 3 Aug 2023 04:20:21 +0200 Subject: [PATCH 07/62] build(Needs bump): Update dependencies --- build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 10131c2..9515ff3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -23,9 +23,9 @@ repositories { } dependencies { - implementation("org.jetbrains.kotlin:kotlin-reflect:1.8.20") + implementation("org.jetbrains.kotlin:kotlin-reflect:1.8.22") - implementation("app.revanced:revanced-patcher:12.0.0") + implementation("app.revanced:revanced-patcher:12.1.0") implementation("info.picocli:picocli:4.7.3") implementation("com.github.revanced:jadb:2531a28109") // updated fork implementation("com.android.tools.build:apksig:8.1.0") From 3f47235c649173a1747754b556e92d47e658810d Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 3 Aug 2023 02:22:52 +0000 Subject: [PATCH 08/62] chore(release): 2.23.0-dev.2 [skip ci] # [2.23.0-dev.2](https://github.com/ReVanced/revanced-cli/compare/v2.23.0-dev.1...v2.23.0-dev.2) (2023-08-03) --- CHANGELOG.md | 2 ++ gradle.properties | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af34b4d..dc4e1af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +# [2.23.0-dev.2](https://github.com/ReVanced/revanced-cli/compare/v2.23.0-dev.1...v2.23.0-dev.2) (2023-08-03) + # [2.23.0-dev.1](https://github.com/ReVanced/revanced-cli/compare/v2.22.1-dev.1...v2.23.0-dev.1) (2023-07-30) diff --git a/gradle.properties b/gradle.properties index ec6ceff..b7bec24 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true kotlin.code.style = official -version = 2.23.0-dev.1 +version = 2.23.0-dev.2 From 9220642a7bcc223256e8f2621ffb041216cbc9a4 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Thu, 3 Aug 2023 20:37:48 +0200 Subject: [PATCH 09/62] build(Needs bump): Bump compatibility --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 9515ff3..3e0ec15 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -25,7 +25,7 @@ repositories { dependencies { implementation("org.jetbrains.kotlin:kotlin-reflect:1.8.22") - implementation("app.revanced:revanced-patcher:12.1.0") + implementation("app.revanced:revanced-patcher:12.1.1") implementation("info.picocli:picocli:4.7.3") implementation("com.github.revanced:jadb:2531a28109") // updated fork implementation("com.android.tools.build:apksig:8.1.0") From 667ca3051c9264460410802cea2449c0038ceb70 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 3 Aug 2023 18:41:14 +0000 Subject: [PATCH 10/62] chore(release): 2.23.0-dev.3 [skip ci] # [2.23.0-dev.3](https://github.com/ReVanced/revanced-cli/compare/v2.23.0-dev.2...v2.23.0-dev.3) (2023-08-03) --- CHANGELOG.md | 2 ++ gradle.properties | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc4e1af..ac65fd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +# [2.23.0-dev.3](https://github.com/ReVanced/revanced-cli/compare/v2.23.0-dev.2...v2.23.0-dev.3) (2023-08-03) + # [2.23.0-dev.2](https://github.com/ReVanced/revanced-cli/compare/v2.23.0-dev.1...v2.23.0-dev.2) (2023-08-03) # [2.23.0-dev.1](https://github.com/ReVanced/revanced-cli/compare/v2.22.1-dev.1...v2.23.0-dev.1) (2023-07-30) diff --git a/gradle.properties b/gradle.properties index b7bec24..05b2782 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true kotlin.code.style = official -version = 2.23.0-dev.2 +version = 2.23.0-dev.3 From 5e73e6d2de7cf9143e377be3a27ae1cdcbb4f554 Mon Sep 17 00:00:00 2001 From: taku Date: Mon, 7 Aug 2023 02:51:01 +0200 Subject: [PATCH 11/62] docs: add missing prerequisites (#239) Co-authored-by: oSumAtrIX --- docs/0_prerequisites.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/0_prerequisites.md b/docs/0_prerequisites.md index bc88231..9f7bbef 100644 --- a/docs/0_prerequisites.md +++ b/docs/0_prerequisites.md @@ -7,9 +7,11 @@ To use ReVanced CLI, you will need to fulfill certain requirements. - Java SDK 11 (Azul JDK or OpenJDK) - [Android Debug Bridge (adb)](https://developer.android.com/studio/command-line/adb) if you want to deploy the patched APK file on your device - An ABI other than ARMv7 such as x86 or x86-64 (or a custom AAPT binary that supports ARMv7) +- ReVanced Patches +- ReVanced Integrations, if the patches require it ## ⏭️ Whats next -The next section will show, how to use [ReVanced CLI](https://github.com/revanced/revanced-cli). +The next section will show you how to use ReVanced CLI Continue: [🛠️ Using ReVanced CLI](1_usage.md) From 4c9d414228f95d39eef0355eb0eb950a040fa08d Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Thu, 10 Aug 2023 02:06:13 +0200 Subject: [PATCH 12/62] docs: fix some mistakes --- docs/0_prerequisites.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/0_prerequisites.md b/docs/0_prerequisites.md index 9f7bbef..d8654d8 100644 --- a/docs/0_prerequisites.md +++ b/docs/0_prerequisites.md @@ -1,10 +1,10 @@ # 💼 Prerequisites -To use ReVanced CLI, you will need to fulfill certain requirements. +To use ReVanced CLI, you will need to fulfil specific requirements. ## 🤝 Requirements -- Java SDK 11 (Azul JDK or OpenJDK) +- Java SDK 11 (Azul Zulu JDK or OpenJDK) - [Android Debug Bridge (adb)](https://developer.android.com/studio/command-line/adb) if you want to deploy the patched APK file on your device - An ABI other than ARMv7 such as x86 or x86-64 (or a custom AAPT binary that supports ARMv7) - ReVanced Patches @@ -12,6 +12,6 @@ To use ReVanced CLI, you will need to fulfill certain requirements. ## ⏭️ Whats next -The next section will show you how to use ReVanced CLI +The following section will show you how to use ReVanced CLI. Continue: [🛠️ Using ReVanced CLI](1_usage.md) From 7174364ef8ef5d6ce8351a8340f9c1a5b58eac3c Mon Sep 17 00:00:00 2001 From: programminghoch10 <16062290+programminghoch10@users.noreply.github.com> Date: Sun, 13 Aug 2023 13:42:16 +0200 Subject: [PATCH 13/62] feat: show full package name when listing patches (#240) --- src/main/kotlin/app/revanced/cli/command/MainCommand.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/app/revanced/cli/command/MainCommand.kt b/src/main/kotlin/app/revanced/cli/command/MainCommand.kt index 0a751d7..38d9e23 100644 --- a/src/main/kotlin/app/revanced/cli/command/MainCommand.kt +++ b/src/main/kotlin/app/revanced/cli/command/MainCommand.kt @@ -245,7 +245,7 @@ internal object MainCommand : Runnable { val packageEntryStr = buildString { // Add package if flag is set if (args.patchArgs?.listingArgs?.withPackages == true) { - val packageName = compatiblePackage.name.substringAfterLast(".").padStart(10) + val packageName = compatiblePackage.name.padStart(25) append(packageName) append("\t") } From 2e3cd9053747170cf90336b3105ed32014b1156c Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Sun, 13 Aug 2023 11:44:20 +0000 Subject: [PATCH 14/62] chore(release): 2.23.0-dev.4 [skip ci] # [2.23.0-dev.4](https://github.com/ReVanced/revanced-cli/compare/v2.23.0-dev.3...v2.23.0-dev.4) (2023-08-13) ### Features * show full package name when listing patches ([#240](https://github.com/ReVanced/revanced-cli/issues/240)) ([7174364](https://github.com/ReVanced/revanced-cli/commit/7174364ef8ef5d6ce8351a8340f9c1a5b58eac3c)) --- CHANGELOG.md | 7 +++++++ gradle.properties | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac65fd3..4aff0ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [2.23.0-dev.4](https://github.com/ReVanced/revanced-cli/compare/v2.23.0-dev.3...v2.23.0-dev.4) (2023-08-13) + + +### Features + +* show full package name when listing patches ([#240](https://github.com/ReVanced/revanced-cli/issues/240)) ([7174364](https://github.com/ReVanced/revanced-cli/commit/7174364ef8ef5d6ce8351a8340f9c1a5b58eac3c)) + # [2.23.0-dev.3](https://github.com/ReVanced/revanced-cli/compare/v2.23.0-dev.2...v2.23.0-dev.3) (2023-08-03) # [2.23.0-dev.2](https://github.com/ReVanced/revanced-cli/compare/v2.23.0-dev.1...v2.23.0-dev.2) (2023-08-03) diff --git a/gradle.properties b/gradle.properties index 05b2782..5c0807e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true kotlin.code.style = official -version = 2.23.0-dev.3 +version = 2.23.0-dev.4 From fc359923e382baff1db3a458e7c1bd52750669ca Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Mon, 14 Aug 2023 04:12:10 +0200 Subject: [PATCH 15/62] build(Needs bump): Bump dependencies --- build.gradle.kts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 3e0ec15..3a0f812 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -11,6 +11,7 @@ val githubPassword: String = project.findProperty("gpr.key") as? String ?: Syste repositories { mavenCentral() mavenLocal() + google() maven { url = uri("https://maven.pkg.github.com/revanced/revanced-patcher") credentials { @@ -19,15 +20,14 @@ repositories { } } maven { url = uri("https://jitpack.io") } - google() } dependencies { implementation("org.jetbrains.kotlin:kotlin-reflect:1.8.22") - implementation("app.revanced:revanced-patcher:12.1.1") + implementation("app.revanced:revanced-patcher:13.0.0") implementation("info.picocli:picocli:4.7.3") - implementation("com.github.revanced:jadb:2531a28109") // updated fork + implementation("com.github.revanced:jadb:2531a28109") // Updated fork implementation("com.android.tools.build:apksig:8.1.0") implementation("org.bouncycastle:bcpkix-jdk15on:1.70") implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.14.3") From ef5fa9b4c94f1d6152b714cc7a6d7b515e152aaf Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Mon, 14 Aug 2023 02:14:18 +0000 Subject: [PATCH 16/62] chore(release): 2.23.0-dev.5 [skip ci] # [2.23.0-dev.5](https://github.com/ReVanced/revanced-cli/compare/v2.23.0-dev.4...v2.23.0-dev.5) (2023-08-14) --- CHANGELOG.md | 2 ++ gradle.properties | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4aff0ed..f994ac3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +# [2.23.0-dev.5](https://github.com/ReVanced/revanced-cli/compare/v2.23.0-dev.4...v2.23.0-dev.5) (2023-08-14) + # [2.23.0-dev.4](https://github.com/ReVanced/revanced-cli/compare/v2.23.0-dev.3...v2.23.0-dev.4) (2023-08-13) diff --git a/gradle.properties b/gradle.properties index 5c0807e..6c37885 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true kotlin.code.style = official -version = 2.23.0-dev.4 +version = 2.23.0-dev.5 From 07da528ce2223582f84bf64d2fec69714c647ddc Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Sat, 19 Aug 2023 01:59:57 +0200 Subject: [PATCH 17/62] refactor!: restructure code This commit focuses on improving code quality in a couple of places and bumping the dependency to ReVanced Patcher. BREAKING CHANGE: This introduces major changes to how ReVanced CLI is used from the command line. --- build.gradle.kts | 4 +- .../app/revanced/cli/command/MainCommand.kt | 383 +++++++++++------- .../app/revanced/cli/patcher/Patcher.kt | 23 -- src/main/kotlin/app/revanced/utils/adb/Adb.kt | 113 ------ .../app/revanced/utils/adb/AdbManager.kt | 130 ++++++ .../kotlin/app/revanced/utils/adb/Commands.kt | 34 +- .../app/revanced/utils/adb/Constants.kt | 47 +-- .../app/revanced/utils/patcher/Patcher.kt | 75 ---- .../patcher/options/PatchOptionOptionsTest.kt | 11 +- 9 files changed, 414 insertions(+), 406 deletions(-) delete mode 100644 src/main/kotlin/app/revanced/cli/patcher/Patcher.kt delete mode 100644 src/main/kotlin/app/revanced/utils/adb/Adb.kt create mode 100644 src/main/kotlin/app/revanced/utils/adb/AdbManager.kt delete mode 100644 src/main/kotlin/app/revanced/utils/patcher/Patcher.kt diff --git a/build.gradle.kts b/build.gradle.kts index 3a0f812..3fa04cb 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -23,9 +23,9 @@ repositories { } dependencies { + implementation("app.revanced:revanced-patcher:14.0.0") implementation("org.jetbrains.kotlin:kotlin-reflect:1.8.22") - - implementation("app.revanced:revanced-patcher:13.0.0") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1") implementation("info.picocli:picocli:4.7.3") implementation("com.github.revanced:jadb:2531a28109") // Updated fork implementation("com.android.tools.build:apksig:8.1.0") diff --git a/src/main/kotlin/app/revanced/cli/command/MainCommand.kt b/src/main/kotlin/app/revanced/cli/command/MainCommand.kt index 38d9e23..5ef13f0 100644 --- a/src/main/kotlin/app/revanced/cli/command/MainCommand.kt +++ b/src/main/kotlin/app/revanced/cli/command/MainCommand.kt @@ -2,28 +2,26 @@ package app.revanced.cli.command import app.revanced.cli.aligning.Aligning import app.revanced.cli.logging.impl.DefaultCliLogger -import app.revanced.cli.patcher.Patcher import app.revanced.cli.patcher.logging.impl.PatcherLogger import app.revanced.cli.signing.Signing import app.revanced.cli.signing.SigningOptions +import app.revanced.patcher.PatchBundleLoader +import app.revanced.patcher.Patcher import app.revanced.patcher.PatcherOptions -import app.revanced.patcher.data.Context import app.revanced.patcher.extensions.PatchExtensions.compatiblePackages import app.revanced.patcher.extensions.PatchExtensions.description +import app.revanced.patcher.extensions.PatchExtensions.include import app.revanced.patcher.extensions.PatchExtensions.patchName -import app.revanced.patcher.patch.Patch -import app.revanced.patcher.util.patch.PatchBundle +import app.revanced.patcher.patch.PatchClass import app.revanced.utils.Options import app.revanced.utils.Options.setOptions -import app.revanced.utils.adb.Adb +import app.revanced.utils.adb.AdbManager +import kotlinx.coroutines.runBlocking import picocli.CommandLine.* import java.io.File -import java.nio.file.Files -/** - * Alias for return type of [PatchBundle.loadPatches]. - */ -internal typealias PatchList = List>> + +internal typealias PatchList = List private class CLIVersionProvider : IVersionProvider { override fun getVersion() = arrayOf( @@ -42,156 +40,192 @@ internal object MainCommand : Runnable { @ArgGroup(exclusive = false, multiplicity = "1") lateinit var args: Args + /** + * Arguments for the CLI + */ class Args { - // TODO: Move this so it is not required when listing patches - @Option(names = ["-a", "--apk"], description = ["APK file to be patched"], required = true) - lateinit var inputFile: File + @Option(names = ["--uninstall"], description = ["Package name to uninstall"]) + var packageName: String? = null - @Option(names = ["--unmount"], description = ["Unmount a patched APK file"]) - var unmount: Boolean = false + @Option(names = ["-d", "--device-serial"], description = ["ADB device serial number to deploy to"]) + var deviceSerial: String? = null - @Option( - names = ["-d", "--deploy"], - description = ["Deploy to the specified device that is connected via ADB"] - ) - var deploy: String? = null + @Option(names = ["--mount"], description = ["Handle deployments by mounting"]) + var mount: Boolean = false @ArgGroup(exclusive = false) var patchArgs: PatchArgs? = null - } - class PatchArgs { - @Option(names = ["-b", "--bundle"], description = ["One or more bundles of patches"], required = true) - var patchBundles = arrayOf() + /** + * Arguments for patches. + */ + class PatchArgs { + @Option(names = ["-b", "--bundle"], description = ["One or more bundles of patches"], required = true) + var patchBundles = emptyList() - @Option(names = ["--options"], description = ["Path to patch options JSON file"]) - var optionsFile: File = File("options.json") + @ArgGroup(exclusive = false) + var listingArgs: ListingArgs? = null - @ArgGroup(exclusive = false) - var listingArgs: ListingArgs? = null + @ArgGroup(exclusive = false) + var patchingArgs: PatchingArgs? = null - @ArgGroup(exclusive = false) - var patchingArgs: PatchingArgs? = null - } + /** + * Arguments for patching. + */ + class PatchingArgs { + @Option(names = ["-a", "--apk"], description = ["APK file to be patched"], required = true) + lateinit var inputFile: File - class ListingArgs { - @Option(names = ["-l", "--list"], description = ["List patches"], required = true) - var listOnly: Boolean = false + @Option( + names = ["-o", "--out"], + description = ["Path to save the patched APK file to"], + required = true + ) + lateinit var outputFilePath: File - @Option(names = ["--with-versions"], description = ["List patches with version compatibilities"]) - var withVersions: Boolean = false + @Option(names = ["--options"], description = ["Path to patch options JSON file"]) + var optionsFile: File = File("options.json") - @Option(names = ["--with-packages"], description = ["List patches with package compatibilities"]) - var withPackages: Boolean = false - } + @Option(names = ["-e", "--exclude"], description = ["List of patches to exclude"]) + var excludedPatches = arrayOf() - class PatchingArgs { - @Option(names = ["-o", "--out"], description = ["Path to save the patched APK file to"], required = true) - lateinit var outputPath: String + @Option( + names = ["--exclusive"], + description = ["Only include patches that are explicitly specified to be included"] + ) + var exclusive = false - @Option(names = ["-e", "--exclude"], description = ["Exclude patches"]) - var excludedPatches = arrayOf() + @Option(names = ["-i", "--include"], description = ["List of patches to include"]) + var includedPatches = arrayOf() - @Option( - names = ["--exclusive"], - description = ["Only include patches that were explicitly specified to be included"] - ) - var exclusive = false + @Option(names = ["--experimental"], description = ["Ignore patches incompatibility to versions"]) + var experimental: Boolean = false - @Option(names = ["-i", "--include"], description = ["Include patches"]) - var includedPatches = arrayOf() + @Option( + names = ["-m", "--merge"], + description = ["One or more DEX files or containers to merge into the APK"] + ) + var integrations = listOf() - @Option(names = ["--experimental"], description = ["Ignore patches incompatibility to versions"]) - var experimental: Boolean = false + @Option(names = ["--cn"], description = ["The common name of the signer of the patched APK file"]) + var commonName = "ReVanced" - @Option(names = ["-m", "--merge"], description = ["One or more DEX files or containers to merge into the APK"]) - var mergeFiles = listOf() + @Option( + names = ["--keystore"], + description = ["Path to the keystore to sign the patched APK file with"] + ) + var keystorePath: String? = null - @Option( - names = ["--mount"], - description = ["Mount the patched APK file over the original file instead of installing it"] - ) - var mount: Boolean = false + @Option( + names = ["-p", "--password"], + description = ["The password of the keystore to sign the patched APK file with"] + ) + var password = "ReVanced" - @Option(names = ["--cn"], description = ["The common name of the signer of the patched APK file"]) - var cn = "ReVanced" + @Option( + names = ["-r", "--resource-cache"], + description = ["Path to temporary resource cache directory"] + ) + var resourceCachePath = File("revanced-resource-cache") - @Option(names = ["--keystore"], description = ["Path to the keystore to sign the patched APK file with"]) - var keystorePath: String? = null + @Option( + names = ["-c", "--clean"], + description = ["Clean up the temporary resource cache directory after patching"] + ) + var clean: Boolean = false - @Option( - names = ["-p", "--password"], - description = ["The password of the keystore to sign the patched APK file with"] - ) - var password = "ReVanced" + @Option( + names = ["--custom-aapt2-binary"], + description = ["Path to a custom AAPT binary to compile resources with"] + ) + var aaptBinaryPath = File("") + } - @Option(names = ["-t", "--temp-dir"], description = ["Path to temporary resource cache directory"]) - var cacheDirectory = "revanced-cache" + /** + * Arguments for printing patches to the console. + */ + class ListingArgs { + @Option(names = ["-l", "--list"], description = ["List patches"], required = true) + var listOnly: Boolean = false - @Option( - names = ["-c", "--clean"], - description = ["Clean up the temporary resource cache directory after patching"] - ) - var clean: Boolean = false + @Option(names = ["--with-versions"], description = ["List patches and their compatible versions"]) + var withVersions: Boolean = false - @Option( - names = ["--custom-aapt2-binary"], - description = ["Path to custom AAPT binary to compile resources with"] - ) - var aaptPath: String = "" + @Option(names = ["--with-packages"], description = ["List patches and their compatible packages"]) + var withPackages: Boolean = false + } + } } override fun run() { - if (args.patchArgs?.listingArgs?.listOnly == true) return printListOfPatches() - if (args.unmount) return unmount() + val patchArgs = args.patchArgs - val pArgs = this.args.patchArgs?.patchingArgs ?: return - val outputFile = File(pArgs.outputPath) // the file to write to + if (patchArgs?.listingArgs?.listOnly == true) return printListOfPatches() + if (args.packageName != null) return uninstall() - val allPatches = args.patchArgs!!.patchBundles.flatMap { bundle -> - PatchBundle.Jar(bundle).loadPatches() + val patchingArgs = patchArgs?.patchingArgs ?: return + + if (!patchingArgs.inputFile.exists()) return logger.error("Input file ${patchingArgs.inputFile} does not exist.") + + logger.info("Loading patches") + + val patches = PatchBundleLoader.Jar(*patchArgs.patchBundles.toTypedArray()) + val integrations = patchingArgs.integrations + + logger.info("Setting up patch options") + + patchingArgs.optionsFile.let { + if (it.exists()) patches.setOptions(it, logger) + else Options.serialize(patches, prettyPrint = true).let(it::writeText) } - args.patchArgs!!.optionsFile.let { - if (it.exists()) allPatches.setOptions(it, logger) - else Options.serialize(allPatches, prettyPrint = true).let(it::writeText) + val adbManager = args.deviceSerial?.let { serial -> + if (args.mount) AdbManager.RootAdbManager(serial, logger) else AdbManager.UserAdbManager(serial, logger) } - val patcher = app.revanced.patcher.Patcher( + val patcher = Patcher( PatcherOptions( - args.inputFile.also { if (!it.exists()) return logger.error("Input file ${args.inputFile} does not exist.") }, - pArgs.cacheDirectory, - pArgs.aaptPath, - pArgs.cacheDirectory, + patchingArgs.inputFile, + patchingArgs.resourceCachePath, + patchingArgs.aaptBinaryPath.absolutePath, + patchingArgs.resourceCachePath.absolutePath, PatcherLogger ) ) - // prepare adb - val adb: Adb? = args.deploy?.let { - Adb(outputFile, patcher.context.packageMetadata.packageName, args.deploy!!, !pArgs.mount) - } + val result = patcher.apply { + acceptIntegrations(integrations) + acceptPatches(filterPatchSelection(patches)) - // start the patcher - val result = Patcher.start(patcher, allPatches) + // Execute patches. + runBlocking { + apply(false).collect { patchResult -> + patchResult.exception?.let { + logger.error("${patchResult.patchName} failed:\n${patchResult.exception}") + } ?: logger.info("${patchResult.patchName} succeeded") + } + } + }.get() - val cacheDirectory = File(pArgs.cacheDirectory) + patcher.close() - // align the file - val alignedFile = cacheDirectory.resolve("${outputFile.nameWithoutExtension}_aligned.apk") - Aligning.align(result, args.inputFile, alignedFile) + val outputFileNameWithoutExtension = patchingArgs.outputFilePath.nameWithoutExtension - // sign the file - val finalFile = if (!pArgs.mount) { - val signedOutput = cacheDirectory.resolve("${outputFile.nameWithoutExtension}_signed.apk") + // Align the file. + val alignedFile = patchingArgs.resourceCachePath.resolve("${outputFileNameWithoutExtension}_aligned.apk") + Aligning.align(result, patchingArgs.inputFile, alignedFile) + + // Sign the file if needed. + val finalFile = if (!args.mount) { + val signedOutput = patchingArgs.resourceCachePath.resolve("${outputFileNameWithoutExtension}_signed.apk") Signing.sign( alignedFile, signedOutput, SigningOptions( - pArgs.cn, - pArgs.password, - pArgs.keystorePath ?: outputFile.absoluteFile.parentFile - .resolve("${outputFile.nameWithoutExtension}.keystore") + patchingArgs.commonName, + patchingArgs.password, + patchingArgs.keystorePath ?: patchingArgs.outputFilePath.absoluteFile.parentFile + .resolve("${patchingArgs.outputFilePath.nameWithoutExtension}.keystore") .canonicalPath ) ) @@ -200,46 +234,41 @@ internal object MainCommand : Runnable { } else alignedFile - // finally copy to the specified output file - logger.info("Copying ${finalFile.name} to ${outputFile.name}") - finalFile.copyTo(outputFile, overwrite = true) + logger.info("Copying ${finalFile.name} to ${patchingArgs.outputFilePath.name}") - // clean up the cache directory if needed - if (pArgs.clean) - cleanUp(pArgs.cacheDirectory) + finalFile.copyTo(patchingArgs.outputFilePath, overwrite = true) + adbManager?.install(AdbManager.Apk(patchingArgs.outputFilePath, patcher.context.packageMetadata.packageName)) - // deploy if specified - adb?.deploy() - - if (pArgs.clean && args.deploy != null) Files.delete(outputFile.toPath()) - - logger.info("Finished") + if (patchingArgs.clean) { + logger.info("Cleaning up temporary files") + patchingArgs.outputFilePath.delete() + cleanUp(patchingArgs.resourceCachePath) + } } - private fun cleanUp(cacheDirectory: String) { - val result = if (File(cacheDirectory).deleteRecursively()) + private fun cleanUp(resourceCachePath: File) { + val result = if (resourceCachePath.deleteRecursively()) "Cleaned up cache directory" else "Failed to clean up cache directory" logger.info(result) } - private fun unmount() { - val adb: Adb? = args.deploy?.let { - Adb( - File("placeholder_file"), - app.revanced.patcher.Patcher(PatcherOptions(args.inputFile, "")).context.packageMetadata.packageName, - args.deploy!!, - false - ) - } - adb?.uninstall() - } + /** + * Uninstall the specified package from the specified device. + * + */ + private fun uninstall() = args.deviceSerial?.let { serial -> + if (args.mount) { + AdbManager.RootAdbManager(serial, logger) + } else { + AdbManager.UserAdbManager(serial, logger) + }.uninstall(args.packageName!!) + } ?: logger.error("No device serial specified") private fun printListOfPatches() { val logged = mutableListOf() - for (patchBundlePath in args.patchArgs?.patchBundles!!) for (patch in PatchBundle.Jar(patchBundlePath) - .loadPatches()) { + for (patch in PatchBundleLoader.Jar(*args.patchArgs!!.patchBundles.toTypedArray())) { if (patch.patchName in logged) continue for (compatiblePackage in patch.compatiblePackages ?: continue) { val packageEntryStr = buildString { @@ -271,4 +300,78 @@ internal object MainCommand : Runnable { } } } -} + + private fun Patcher.filterPatchSelection(patches: PatchList) = buildList { + val packageName = context.packageMetadata.packageName + val packageVersion = context.packageMetadata.packageVersion + val patchingArgs = args.patchArgs!!.patchingArgs!! + + patches.forEach patch@{ patch -> + val formattedPatchName = patch.patchName.lowercase().replace(" ", "-") + + /** + * Check if the patch is explicitly excluded. + * + * Cases: + * 1. -e patch.name + * 2. -i patch.name -e patch.name + */ + + val excluded = patchingArgs.excludedPatches.contains(formattedPatchName) + if (excluded) return@patch logger.info("Excluding ${patch.patchName}") + + /** + * Check if the patch is constrained to packages. + */ + + patch.compatiblePackages?.let { packages -> + packages.singleOrNull { it.name == packageName }?.let { `package` -> + /** + * Check if the package version matches. + * If experimental is true, version matching will be skipped. + */ + + val matchesVersion = patchingArgs.experimental || `package`.versions.let { + it.isEmpty() || it.any { version -> version == packageVersion } + } + + if (!matchesVersion) return@patch logger.warn( + "${patch.patchName} is incompatible with version $packageVersion. " + + "This patch is only compatible with version " + + packages.joinToString(";") { `package` -> + "${`package`.name}: ${`package`.versions.joinToString(", ")}" + } + ) + + } ?: return@patch logger.trace( + "${patch.patchName} is incompatible with $packageName. " + + "This patch is only compatible with " + + packages.joinToString(", ") { `package` -> `package`.name } + ) + + return@let + } ?: logger.trace("$formattedPatchName: No constraint on packages.") + + /** + * Check if the patch is explicitly included. + * + * Cases: + * 1. --exclusive + * 2. --exclusive -i patch.name + */ + + val exclusive = patchingArgs.exclusive + val explicitlyIncluded = patchingArgs.includedPatches.contains(formattedPatchName) + + val implicitlyIncluded = !exclusive && patch.include // Case 3. + val exclusivelyIncluded = exclusive && explicitlyIncluded // Case 2. + + val included = implicitlyIncluded || exclusivelyIncluded + if (!included) return@patch logger.info("${patch.patchName} excluded by default") // Case 1. + + logger.trace("Adding $formattedPatchName") + + add(patch) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/cli/patcher/Patcher.kt b/src/main/kotlin/app/revanced/cli/patcher/Patcher.kt deleted file mode 100644 index 180703e..0000000 --- a/src/main/kotlin/app/revanced/cli/patcher/Patcher.kt +++ /dev/null @@ -1,23 +0,0 @@ -package app.revanced.cli.patcher - -import app.revanced.cli.command.PatchList -import app.revanced.patcher.PatcherResult -import app.revanced.utils.patcher.addPatchesFiltered -import app.revanced.utils.patcher.applyPatchesVerbose -import app.revanced.utils.patcher.mergeFiles - -internal object Patcher { - internal fun start( - patcher: app.revanced.patcher.Patcher, - allPatches: PatchList - ): PatcherResult { - // merge files like necessary integrations - patcher.mergeFiles() - // add patches, but filter incompatible or excluded patches - patcher.addPatchesFiltered(allPatches) - // apply patches - patcher.applyPatchesVerbose() - - return patcher.save() - } -} diff --git a/src/main/kotlin/app/revanced/utils/adb/Adb.kt b/src/main/kotlin/app/revanced/utils/adb/Adb.kt deleted file mode 100644 index 3c51566..0000000 --- a/src/main/kotlin/app/revanced/utils/adb/Adb.kt +++ /dev/null @@ -1,113 +0,0 @@ -package app.revanced.utils.adb - -import app.revanced.cli.command.MainCommand.logger -import se.vidstige.jadb.JadbConnection -import se.vidstige.jadb.JadbDevice -import se.vidstige.jadb.managers.PackageManager -import java.io.File -import java.util.concurrent.Executors - -internal class Adb( - private val file: File, - private val packageName: String, - deviceName: String, - private val install: Boolean = false, - private val logging: Boolean = true -) { - private val device: JadbDevice - - init { - device = JadbConnection().devices.let { device -> device.find { it.serial == deviceName } ?: device.first() } - ?: throw IllegalArgumentException("No such device with name $deviceName") - - if (!install && device.run("su -h", false) != 0) - throw IllegalArgumentException("Root required on $deviceName. Task failed") - } - - private fun String.replacePlaceholder(with: String? = null): String { - return this.replace(Constants.PLACEHOLDER, with ?: packageName) - } - - internal fun deploy() { - if (install) { - logger.info("Installing without mounting") - - PackageManager(device).install(file) - } else { - logger.info("Installing by mounting") - - // push patched file - device.copy(Constants.PATH_INIT_PUSH, file) - - // create revanced folder path - device.run("${Constants.COMMAND_CREATE_DIR} ${Constants.PATH_REVANCED}") - - // prepare mounting the apk - device.run(Constants.COMMAND_PREPARE_MOUNT_APK.replacePlaceholder()) - - // push mount script - device.createFile( - Constants.PATH_INIT_PUSH, - Constants.CONTENT_MOUNT_SCRIPT.replacePlaceholder() - ) - // install mount script - device.run(Constants.COMMAND_INSTALL_MOUNT.replacePlaceholder()) - - // unmount the apk for sanity - device.run(Constants.COMMAND_UMOUNT.replacePlaceholder()) - // mount the apk - device.run(Constants.PATH_MOUNT.replacePlaceholder()) - - // relaunch app - device.run(Constants.COMMAND_RESTART.replacePlaceholder()) - - // log the app - log() - } - } - - internal fun uninstall() { - logger.info("Uninstalling by unmounting") - - // unmount the apk - device.run(Constants.COMMAND_UMOUNT.replacePlaceholder()) - - // delete revanced app - device.run(Constants.COMMAND_DELETE.replacePlaceholder(Constants.PATH_REVANCED_APP).replacePlaceholder()) - - // delete mount script - device.run(Constants.COMMAND_DELETE.replacePlaceholder(Constants.PATH_MOUNT).replacePlaceholder()) - - logger.info("Finished uninstalling") - } - - private fun log() { - val executor = Executors.newSingleThreadExecutor() - val pipe = if (logging) { - ProcessBuilder.Redirect.INHERIT - } else { - ProcessBuilder.Redirect.PIPE - } - - val process = device.buildCommand(Constants.COMMAND_LOGCAT.replacePlaceholder()) - .redirectOutput(pipe) - .redirectError(pipe) - .useExecutor(executor) - .start() - - Thread.sleep(500) // give the app some time to start up. - while (true) { - try { - while (device.run("${Constants.COMMAND_PID_OF} $packageName") == 0) { - Thread.sleep(1000) - } - break - } catch (e: Exception) { - throw RuntimeException("An error occurred while monitoring the state of app", e) - } - } - logger.info("Stopped logging because the app was closed") - process.destroy() - executor.shutdown() - } -} diff --git a/src/main/kotlin/app/revanced/utils/adb/AdbManager.kt b/src/main/kotlin/app/revanced/utils/adb/AdbManager.kt new file mode 100644 index 0000000..1bbc28d --- /dev/null +++ b/src/main/kotlin/app/revanced/utils/adb/AdbManager.kt @@ -0,0 +1,130 @@ +package app.revanced.utils.adb + +import app.revanced.cli.logging.CliLogger +import app.revanced.utils.adb.AdbManager.Apk +import app.revanced.utils.adb.Constants.COMMAND_CREATE_DIR +import app.revanced.utils.adb.Constants.COMMAND_DELETE +import app.revanced.utils.adb.Constants.COMMAND_INSTALL_MOUNT +import app.revanced.utils.adb.Constants.COMMAND_PREPARE_MOUNT_APK +import app.revanced.utils.adb.Constants.COMMAND_RESTART +import app.revanced.utils.adb.Constants.COMMAND_UMOUNT +import app.revanced.utils.adb.Constants.CONTENT_MOUNT_SCRIPT +import app.revanced.utils.adb.Constants.PATH_INIT_PUSH +import app.revanced.utils.adb.Constants.PATH_INSTALLATION +import app.revanced.utils.adb.Constants.PATH_MOUNT +import app.revanced.utils.adb.Constants.PATH_PATCHED_APK +import app.revanced.utils.adb.Constants.PLACEHOLDER +import se.vidstige.jadb.JadbConnection +import se.vidstige.jadb.managers.Package +import se.vidstige.jadb.managers.PackageManager +import java.io.Closeable +import java.io.File + +/** + * Adb manager. Used to install and uninstall [Apk] files. + * + * @param deviceSerial The serial of the device. + */ +internal sealed class AdbManager(deviceSerial: String? = null, protected val logger: CliLogger? = null) : Closeable { + protected val device = JadbConnection().devices.find { device -> device.serial == deviceSerial } + ?: throw IllegalArgumentException("The device with the serial $deviceSerial can not be found.") + + init { + logger?.trace("Established connection to $deviceSerial") + } + + /** + * Installs the [Apk] file. + * + * @param apk The [Apk] file. + */ + open fun install(apk: Apk) { + logger?.info("Finished installing ${apk.file.name}") + } + + /** + * Uninstalls the package. + * + * @param packageName The package name. + */ + open fun uninstall(packageName: String) { + logger?.info("Finished uninstalling $packageName") + } + + /** + * Closes the [AdbManager] instance. + */ + override fun close() { + logger?.trace("Closed") + } + + class RootAdbManager(deviceSerial: String, logger: CliLogger? = null) : AdbManager(deviceSerial, logger) { + init { + if (!device.hasSu()) throw IllegalArgumentException("Root required on $deviceSerial. Task failed") + } + + override fun install(apk: Apk) { + logger?.info("Installing by mounting") + + val applyReplacement = getPlaceholderReplacement( + apk.packageName ?: throw IllegalArgumentException("Package name is required") + ) + + device.copyFile(apk.file, PATH_INIT_PUSH) + + device.run("$COMMAND_CREATE_DIR $PATH_INSTALLATION") + device.run(COMMAND_PREPARE_MOUNT_APK.applyReplacement()) + + device.createFile(PATH_INIT_PUSH, CONTENT_MOUNT_SCRIPT.applyReplacement()) + + device.run(COMMAND_INSTALL_MOUNT.applyReplacement()) + device.run(COMMAND_UMOUNT.applyReplacement()) // Sanity check. + device.run(PATH_MOUNT.applyReplacement()) + device.run(COMMAND_RESTART.applyReplacement()) + + super.install(apk) + } + + override fun uninstall(packageName: String) { + logger?.info("Uninstalling $packageName by unmounting and deleting the package") + + val applyReplacement = getPlaceholderReplacement(packageName) + + device.run(COMMAND_UMOUNT.applyReplacement(packageName)) + device.run(COMMAND_DELETE.applyReplacement(PATH_PATCHED_APK).applyReplacement()) + device.run(COMMAND_DELETE.applyReplacement(PATH_MOUNT).applyReplacement()) + + super.uninstall(packageName) + } + + companion object Utils { + private fun getPlaceholderReplacement(with: String): String.() -> String = { replace(PLACEHOLDER, with) } + private fun String.applyReplacement(with: String) = replace(PLACEHOLDER, with) + } + } + + class UserAdbManager(deviceSerial: String, logger: CliLogger? = null) : AdbManager(deviceSerial, logger) { + private val packageManager = PackageManager(device) + + override fun install(apk: Apk) { + PackageManager(device).install(apk.file) + + super.install(apk) + } + + override fun uninstall(packageName: String) { + logger?.info("Uninstalling $packageName") + + packageManager.uninstall(Package(packageName)) + + super.uninstall(packageName) + } + } + + /** + * Apk file for [AdbManager]. + * + * @param file The [Apk] file. + */ + internal class Apk(val file: File, val packageName: String? = null) +} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/utils/adb/Commands.kt b/src/main/kotlin/app/revanced/utils/adb/Commands.kt index 1b3af07..744ebda 100644 --- a/src/main/kotlin/app/revanced/utils/adb/Commands.kt +++ b/src/main/kotlin/app/revanced/utils/adb/Commands.kt @@ -2,28 +2,28 @@ package app.revanced.utils.adb import se.vidstige.jadb.JadbDevice import se.vidstige.jadb.RemoteFile -import se.vidstige.jadb.ShellProcessBuilder import java.io.File +import java.util.concurrent.Callable +import java.util.concurrent.Executors -internal fun JadbDevice.buildCommand(command: String, su: Boolean = true): ShellProcessBuilder { - if (su) { - return shellProcessBuilder("su -c \'$command\'") +// return the input or output stream, depending on which first returns a value +internal fun JadbDevice.run(command: String, su: Boolean = false) = with(this.startCommand(command, su)) { + Executors.newFixedThreadPool(2).let { service -> + arrayOf(inputStream, errorStream).map { stream -> + Callable { stream.bufferedReader().use { it.readLine() } } + }.let { tasks -> service.invokeAny(tasks).also { service.shutdown() } } } - - val args = command.split(" ") as ArrayList - val cmd = args.removeFirst() - - return shellProcessBuilder(cmd, *args.toTypedArray()) } -internal fun JadbDevice.run(command: String, su: Boolean = true): Int { - return this.buildCommand(command, su).start().waitFor() -} +internal fun JadbDevice.hasSu() = + this.startCommand("su -h", false).waitFor() == 0 -internal fun JadbDevice.copy(targetPath: String, file: File) { - push(file, RemoteFile(targetPath)) -} +internal fun JadbDevice.copyFile(file: File, targetFile: String) = + push(file, RemoteFile(targetFile)) -internal fun JadbDevice.createFile(targetFile: String, content: String) { +internal fun JadbDevice.createFile(targetFile: String, content: String) = push(content.byteInputStream(), System.currentTimeMillis(), 644, RemoteFile(targetFile)) -} \ No newline at end of file + + +private fun JadbDevice.startCommand(command: String, su: Boolean) = + shellProcessBuilder(if (su) "su -c '$command'" else command).start() \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/utils/adb/Constants.kt b/src/main/kotlin/app/revanced/utils/adb/Constants.kt index f4aa5f2..0d97211 100644 --- a/src/main/kotlin/app/revanced/utils/adb/Constants.kt +++ b/src/main/kotlin/app/revanced/utils/adb/Constants.kt @@ -1,57 +1,40 @@ package app.revanced.utils.adb internal object Constants { - // template placeholder to replace a string in commands internal const val PLACEHOLDER = "TEMPLATE_PACKAGE_NAME" - // utility commands - private const val COMMAND_CHMOD_MOUNT = "chmod +x" - internal const val COMMAND_PID_OF = "pidof -s" - internal const val COMMAND_CREATE_DIR = "mkdir -p" - internal const val COMMAND_LOGCAT = "logcat -c && logcat | grep AndroidRuntime" - internal const val COMMAND_RESTART = "pm resolve-activity --brief $PLACEHOLDER | tail -n 1 | xargs am start -n && kill ${'$'}($COMMAND_PID_OF $PLACEHOLDER)" - - // default mount file name - private const val NAME_MOUNT_SCRIPT = "mount_revanced_$PLACEHOLDER.sh" - - // initial directory to push files to via adb push internal const val PATH_INIT_PUSH = "/data/local/tmp/revanced.delete" + internal const val PATH_INSTALLATION = "/data/adb/revanced/" + internal const val PATH_PATCHED_APK = "$PATH_INSTALLATION$PLACEHOLDER.apk" + internal const val PATH_MOUNT = "/data/adb/service.d/mount_revanced_$PLACEHOLDER.sh" - // revanced path - internal const val PATH_REVANCED = "/data/adb/revanced/" - - // revanced apk path - internal const val PATH_REVANCED_APP = "$PATH_REVANCED$PLACEHOLDER.apk" - - // delete command internal const val COMMAND_DELETE = "rm -rf $PLACEHOLDER" + internal const val COMMAND_CREATE_DIR = "mkdir -p" + internal const val COMMAND_RESTART = "pm resolve-activity --brief $PLACEHOLDER | tail -n 1 | " + + "xargs am start -n && kill ${'$'}(pidof -s $PLACEHOLDER)" - // mount script path - internal const val PATH_MOUNT = "/data/adb/service.d/$NAME_MOUNT_SCRIPT" + internal const val COMMAND_PREPARE_MOUNT_APK = "base_path=\"$PATH_PATCHED_APK\" && " + + "mv $PATH_INIT_PUSH ${'$'}base_path && " + + "chmod 644 ${'$'}base_path && " + + "chown system:system ${'$'}base_path && " + + "chcon u:object_r:apk_data_file:s0 ${'$'}base_path" - // move to revanced apk path & set permissions - internal const val COMMAND_PREPARE_MOUNT_APK = - "base_path=\"$PATH_REVANCED_APP\" && mv $PATH_INIT_PUSH ${'$'}base_path && chmod 644 ${'$'}base_path && chown system:system ${'$'}base_path && chcon u:object_r:apk_data_file:s0 ${'$'}base_path" - - // unmount command internal const val COMMAND_UMOUNT = "grep $PLACEHOLDER /proc/mounts | while read -r line; do echo ${'$'}line | cut -d \" \" -f 2 | sed 's/apk.*/apk/' | xargs -r umount -l; done" - // install mount script & set permissions - internal const val COMMAND_INSTALL_MOUNT = "mv $PATH_INIT_PUSH $PATH_MOUNT && $COMMAND_CHMOD_MOUNT $PATH_MOUNT" + internal const val COMMAND_INSTALL_MOUNT = "mv $PATH_INIT_PUSH $PATH_MOUNT && chmod +x $PATH_MOUNT" - // mount script - internal val CONTENT_MOUNT_SCRIPT = + internal const val CONTENT_MOUNT_SCRIPT = """ #!/system/bin/sh MAGISKTMP="${'$'}(magisk --path)" || MAGISKTMP=/sbin MIRROR="${'$'}MAGISKTMP/.magisk/mirror" while [ "${'$'}(getprop sys.boot_completed | tr -d '\r')" != "1" ]; do sleep 1; done - base_path="$PATH_REVANCED_APP" + base_path="$PATH_PATCHED_APK" stock_path=${'$'}( pm path $PLACEHOLDER | grep base | sed 's/package://g' ) chcon u:object_r:apk_data_file:s0 ${'$'}base_path mount -o bind ${'$'}MIRROR${'$'}base_path ${'$'}stock_path - """.trimIndent() + """ } diff --git a/src/main/kotlin/app/revanced/utils/patcher/Patcher.kt b/src/main/kotlin/app/revanced/utils/patcher/Patcher.kt deleted file mode 100644 index fd61bae..0000000 --- a/src/main/kotlin/app/revanced/utils/patcher/Patcher.kt +++ /dev/null @@ -1,75 +0,0 @@ -package app.revanced.utils.patcher - -import app.revanced.cli.command.MainCommand.args -import app.revanced.cli.command.MainCommand.logger -import app.revanced.cli.command.PatchList -import app.revanced.patcher.Patcher -import app.revanced.patcher.data.Context -import app.revanced.patcher.extensions.PatchExtensions.compatiblePackages -import app.revanced.patcher.extensions.PatchExtensions.include -import app.revanced.patcher.extensions.PatchExtensions.patchName -import app.revanced.patcher.patch.Patch - -fun Patcher.addPatchesFiltered(allPatches: PatchList) { - val packageName = this.context.packageMetadata.packageName - val packageVersion = this.context.packageMetadata.packageVersion - - val includedPatches = mutableListOf>>() - allPatches.forEach patchLoop@{ patch -> - val compatiblePackages = patch.compatiblePackages - val args = args.patchArgs?.patchingArgs!! - - val prefix = "Skipping ${patch.patchName}" - - if (compatiblePackages == null) logger.trace("${patch.patchName}: No package constraints.") - else { - if (!compatiblePackages.any { it.name == packageName }) { - logger.trace("$prefix: Incompatible with $packageName. This patch is only compatible with ${ - compatiblePackages.joinToString( - ", " - ) { it.name } - }") - return@patchLoop - } - - if (!(args.experimental || compatiblePackages.any { it.versions.isEmpty() || it.versions.any { version -> version == packageVersion } })) { - val compatibleWith = compatiblePackages.joinToString(";") { _package -> - "${_package.name}: ${_package.versions.joinToString(", ")}" - } - logger.warn("$prefix: Incompatible with version $packageVersion. This patch is only compatible with $compatibleWith") - return@patchLoop - } - } - - val kebabCasedPatchName = patch.patchName.lowercase().replace(" ", "-") - if (args.excludedPatches.contains(kebabCasedPatchName)) { - logger.info("$prefix: Manually excluded") - return@patchLoop - } else if ((!patch.include || args.exclusive) && !args.includedPatches.contains(kebabCasedPatchName)) { - logger.info("$prefix: Excluded by default") - return@patchLoop - } - - logger.trace("Adding ${patch.patchName}") - includedPatches.add(patch) - } - - this.addPatches(includedPatches) -} - -fun Patcher.applyPatchesVerbose() { - this.executePatches().forEach { (patch, result) -> - if (result.isSuccess) { - logger.info("$patch succeeded") - return@forEach - } - logger.error("$patch failed:") - result.exceptionOrNull()!!.printStackTrace() - } -} - -fun Patcher.mergeFiles() { - this.addIntegrations(args.patchArgs?.patchingArgs!!.mergeFiles) { file -> - logger.info("Merging $file") - } -} diff --git a/src/test/kotlin/app/revanced/patcher/options/PatchOptionOptionsTest.kt b/src/test/kotlin/app/revanced/patcher/options/PatchOptionOptionsTest.kt index 68759ee..9abfca5 100644 --- a/src/test/kotlin/app/revanced/patcher/options/PatchOptionOptionsTest.kt +++ b/src/test/kotlin/app/revanced/patcher/options/PatchOptionOptionsTest.kt @@ -2,7 +2,10 @@ package app.revanced.patcher.options import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.data.Context -import app.revanced.patcher.patch.* +import app.revanced.patcher.patch.BytecodePatch +import app.revanced.patcher.patch.OptionsContainer +import app.revanced.patcher.patch.Patch +import app.revanced.patcher.patch.PatchOption import app.revanced.utils.Options import app.revanced.utils.Options.setOptions import org.junit.jupiter.api.MethodOrderer @@ -11,8 +14,8 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestMethodOrder class PatchOptionsTestPatch : BytecodePatch() { - override fun execute(context: BytecodeContext): PatchResult { - return PatchResultSuccess() + override fun execute(context: BytecodeContext) { + // Do nothing } companion object : OptionsContainer() { @@ -32,7 +35,7 @@ class PatchOptionsTestPatch : BytecodePatch() { @TestMethodOrder(MethodOrderer.OrderAnnotation::class) internal object PatchOptionOptionsTest { - private var patches = listOf(PatchOptionsTestPatch::class.java as Class>) + private var patches = listOf(PatchOptionsTestPatch::class.java as Class>>) @Test @Order(1) From fe75d4ab87aca686b277baf0d9f2e676bcce3ae0 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Tue, 22 Aug 2023 22:59:07 +0200 Subject: [PATCH 18/62] docs: use correct description --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0810591..0b6f6f6 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ # 💻 ReVanced CLI -Command line application as an alternative to the ReVanced Manager. +Command line application to use ReVanced. From b74213f66e0d04d3a0ae6197d069631388e06580 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Tue, 22 Aug 2023 22:59:32 +0200 Subject: [PATCH 19/62] feat: use separate command to list patches --- docs/1_usage.md | 11 ++- .../cli/command/ListPatchesCommand.kt | 94 +++++++++++++++++++ .../app/revanced/cli/command/MainCommand.kt | 68 +++----------- src/main/kotlin/app/revanced/cli/main/Main.kt | 8 -- 4 files changed, 113 insertions(+), 68 deletions(-) create mode 100644 src/main/kotlin/app/revanced/cli/command/ListPatchesCommand.kt delete mode 100644 src/main/kotlin/app/revanced/cli/main/Main.kt diff --git a/docs/1_usage.md b/docs/1_usage.md index a3226de..2b81de6 100644 --- a/docs/1_usage.md +++ b/docs/1_usage.md @@ -30,12 +30,15 @@ Learn how to ReVanced CLI. java -jar revanced-cli.jar -h ``` -- ### 📃 List all available patches from supplied patch bundles +- ### 📃 List patches from supplied patch bundles ```bash - java -jar revanced-cli.jar - -b revanced-patches.jar \ - -l # Names of all patches will be in kebab-case + java -jar revanced-cli.jar \ + list-patches \ + --with-packages \ + --with-versions \ + --with-options \ + revanced-patches.jar ``` - ### 💉 Use ReVanced CLI to patch an APK file but deploy without root permissions diff --git a/src/main/kotlin/app/revanced/cli/command/ListPatchesCommand.kt b/src/main/kotlin/app/revanced/cli/command/ListPatchesCommand.kt new file mode 100644 index 0000000..9c42595 --- /dev/null +++ b/src/main/kotlin/app/revanced/cli/command/ListPatchesCommand.kt @@ -0,0 +1,94 @@ +package app.revanced.cli.command + +import app.revanced.patcher.PatchBundleLoader +import app.revanced.patcher.annotation.Package +import app.revanced.patcher.extensions.PatchExtensions.compatiblePackages +import app.revanced.patcher.extensions.PatchExtensions.description +import app.revanced.patcher.extensions.PatchExtensions.options +import app.revanced.patcher.extensions.PatchExtensions.patchName +import app.revanced.patcher.patch.PatchClass +import app.revanced.patcher.patch.PatchOption +import picocli.CommandLine +import picocli.CommandLine.Help.Visibility.ALWAYS +import java.io.File + + +@CommandLine.Command(name = "list-patches", description = ["List patches from supplied patch bundles"]) +class ListPatchesCommand : Runnable { + @CommandLine.Parameters( + description = ["Paths to patch bundles"], + arity = "1..*" + ) + lateinit var patchBundles: Array + + @CommandLine.Option( + names = ["-d", "--with-descriptions"], + description = ["List their descriptions"], + showDefaultValue = ALWAYS + ) + var withDescriptions: Boolean = true + + @CommandLine.Option( + names = ["-p", "--with-packages"], + description = ["List the packages the patches are compatible with"], + showDefaultValue = ALWAYS + ) + var withPackages: Boolean = false + + @CommandLine.Option( + names = ["-v", "--with-versions"], + description = ["List the versions of the packages the patches are compatible with"], + showDefaultValue = ALWAYS + ) + var withVersions: Boolean = false + + @CommandLine.Option( + names = ["-o", "--with-options"], + description = ["List the options of the patches"], + showDefaultValue = ALWAYS + ) + var withOptions: Boolean = false + + override fun run() { + fun Package.buildString() = buildString { + if (withVersions && versions.isNotEmpty()) { + appendLine("Package name: $name") + appendLine("Compatible versions:") + append(versions.joinToString("\n") { version -> version }.prependIndent("\t")) + } else + append("Package name: $name") + } + + fun PatchOption<*>.buildString() = buildString { + appendLine("Title: $title") + appendLine("Description: $description") + + value?.let { + appendLine("Key: $key") + append("Value: $it") + } ?: append("Key: $key") + } + + fun PatchClass.buildString() = buildString { + append("Name: $patchName") + + if (withDescriptions) append("\nDescription: $description") + + if (withOptions && options != null) { + appendLine("\nOptions:") + append( + options!!.joinToString("\n\n") { option -> option.buildString() }.prependIndent("\t") + ) + } + + if (withPackages && compatiblePackages != null) { + appendLine("\nCompatible packages:") + append( + compatiblePackages!!.joinToString("\n") { it.buildString() }.prependIndent("\t") + ) + } + } + + MainCommand.logger.info(PatchBundleLoader.Jar(*patchBundles).joinToString("\n\n") { it.buildString() }) + } +} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/cli/command/MainCommand.kt b/src/main/kotlin/app/revanced/cli/command/MainCommand.kt index 5ef13f0..8394a7b 100644 --- a/src/main/kotlin/app/revanced/cli/command/MainCommand.kt +++ b/src/main/kotlin/app/revanced/cli/command/MainCommand.kt @@ -9,7 +9,6 @@ import app.revanced.patcher.PatchBundleLoader import app.revanced.patcher.Patcher import app.revanced.patcher.PatcherOptions import app.revanced.patcher.extensions.PatchExtensions.compatiblePackages -import app.revanced.patcher.extensions.PatchExtensions.description import app.revanced.patcher.extensions.PatchExtensions.include import app.revanced.patcher.extensions.PatchExtensions.patchName import app.revanced.patcher.patch.PatchClass @@ -17,9 +16,13 @@ import app.revanced.utils.Options import app.revanced.utils.Options.setOptions import app.revanced.utils.adb.AdbManager import kotlinx.coroutines.runBlocking +import picocli.CommandLine import picocli.CommandLine.* import java.io.File +fun main(args: Array) { + CommandLine(MainCommand).execute(*args) +} internal typealias PatchList = List @@ -31,13 +34,15 @@ private class CLIVersionProvider : IVersionProvider { @Command( name = "ReVanced CLI", + description = ["Command line application to use ReVanced"], mixinStandardHelpOptions = true, - versionProvider = CLIVersionProvider::class + versionProvider = CLIVersionProvider::class, + subcommands = [ListPatchesCommand::class] ) internal object MainCommand : Runnable { val logger = DefaultCliLogger() - @ArgGroup(exclusive = false, multiplicity = "1") + // @ArgGroup(exclusive = false, multiplicity = "1") lateinit var args: Args /** @@ -63,9 +68,6 @@ internal object MainCommand : Runnable { @Option(names = ["-b", "--bundle"], description = ["One or more bundles of patches"], required = true) var patchBundles = emptyList() - @ArgGroup(exclusive = false) - var listingArgs: ListingArgs? = null - @ArgGroup(exclusive = false) var patchingArgs: PatchingArgs? = null @@ -140,27 +142,12 @@ internal object MainCommand : Runnable { ) var aaptBinaryPath = File("") } - - /** - * Arguments for printing patches to the console. - */ - class ListingArgs { - @Option(names = ["-l", "--list"], description = ["List patches"], required = true) - var listOnly: Boolean = false - - @Option(names = ["--with-versions"], description = ["List patches and their compatible versions"]) - var withVersions: Boolean = false - - @Option(names = ["--with-packages"], description = ["List patches and their compatible packages"]) - var withPackages: Boolean = false - } } } override fun run() { val patchArgs = args.patchArgs - if (patchArgs?.listingArgs?.listOnly == true) return printListOfPatches() if (args.packageName != null) return uninstall() val patchingArgs = patchArgs?.patchingArgs ?: return @@ -266,41 +253,6 @@ internal object MainCommand : Runnable { }.uninstall(args.packageName!!) } ?: logger.error("No device serial specified") - private fun printListOfPatches() { - val logged = mutableListOf() - for (patch in PatchBundleLoader.Jar(*args.patchArgs!!.patchBundles.toTypedArray())) { - if (patch.patchName in logged) continue - for (compatiblePackage in patch.compatiblePackages ?: continue) { - val packageEntryStr = buildString { - // Add package if flag is set - if (args.patchArgs?.listingArgs?.withPackages == true) { - val packageName = compatiblePackage.name.padStart(25) - append(packageName) - append("\t") - } - - // Add patch name - val patchName = patch.patchName.lowercase().replace(" ", "-").padStart(25) - append(patchName) - - // Add description if flag is set. - append("\t") - append(patch.description) - - // Add compatible versions, if flag is set - if (args.patchArgs?.listingArgs?.withVersions == true) { - val compatibleVersions = compatiblePackage.versions.joinToString(separator = ", ") - append("\t") - append(compatibleVersions) - } - } - - logged.add(patch.patchName) - logger.info(packageEntryStr) - } - } - } - private fun Patcher.filterPatchSelection(patches: PatchList) = buildList { val packageName = context.packageMetadata.packageName val packageVersion = context.packageMetadata.packageVersion @@ -374,4 +326,8 @@ internal object MainCommand : Runnable { add(patch) } } + + fun main(args: Array) { + CommandLine(MainCommand).execute(*args) + } } \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/cli/main/Main.kt b/src/main/kotlin/app/revanced/cli/main/Main.kt deleted file mode 100644 index 3fc7b3d..0000000 --- a/src/main/kotlin/app/revanced/cli/main/Main.kt +++ /dev/null @@ -1,8 +0,0 @@ -package app.revanced.cli.main - -import app.revanced.cli.command.MainCommand -import picocli.CommandLine - -internal fun main(args: Array) { - CommandLine(MainCommand).execute(*args) -} \ No newline at end of file From c0cc90962646cfffd5e2730ae556423271a7990b Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Tue, 22 Aug 2023 23:35:02 +0200 Subject: [PATCH 20/62] feat: use separate command to uninstall --- docs/1_usage.md | 27 ++++++++---- .../app/revanced/cli/command/MainCommand.kt | 27 +++--------- .../revanced/cli/command/UninstallCommand.kt | 44 +++++++++++++++++++ .../app/revanced/utils/adb/AdbManager.kt | 7 ++- 4 files changed, 74 insertions(+), 31 deletions(-) create mode 100644 src/main/kotlin/app/revanced/cli/command/UninstallCommand.kt diff --git a/docs/1_usage.md b/docs/1_usage.md index 2b81de6..932fdb2 100644 --- a/docs/1_usage.md +++ b/docs/1_usage.md @@ -41,6 +41,14 @@ Learn how to ReVanced CLI. revanced-patches.jar ``` +- ### ⚙️ Supply options to patches using ReVanced CLI + + Some patches provide options. Currently, ReVanced CLI will generate and consume an `options.json` file at the location that is specified in `-o`. If the option is not specified, the options file will be generated in the current working directory. + + The options file contains all options from supplied patch bundles. + + > **Note**: The `options.json` file will be generated at the first time you use ReVanced CLI to patch an APK file for now. This will be changed in the future. + - ### 💉 Use ReVanced CLI to patch an APK file but deploy without root permissions This will deploy the patched APK file on your device by installing it. @@ -50,7 +58,7 @@ Learn how to ReVanced CLI. -a input.apk \ -o patched-output.apk \ -b revanced-patches.jar \ - -d device-name + -d device-serial ``` - ### 👾 Use ReVanced CLI to patch an APK file but deploy with root permissions @@ -64,16 +72,17 @@ Learn how to ReVanced CLI. -o patched-output.apk \ -b revanced-patches.jar \ -e vanced-microg-support \ - -d device-name \ + -d device-serial \ --mount ``` > **Note**: Some patches from [ReVanced Patches](https://github.com/revanced/revanced-patches) also require [ReVanced Integrations](https://github.com/revanced/revanced-integrations). Supply them with the option `-m`. ReVanced Patcher will merge ReVanced Integrations automatically, depending on if the supplied patches require them. + package -- ### ⚙️ Supply options to patches using ReVanced CLI - - Some patches provide options. Currently, ReVanced CLI will generate and consume an `options.json` file at the location that is specified in `-o`. If the option is not specified, the options file will be generated in the current working directory. - - The options file contains all options from supplied patch bundles. - - > **Note**: The `options.json` file will be generated at the first time you use ReVanced CLI to patch an APK file for now. This will be changed in the future. +- ### 🗑️ Uninstall a patched + ```bash + java -jar revanced-cli.jar \ + uninstall \ + -p package-name \ + device-serial + ``` diff --git a/src/main/kotlin/app/revanced/cli/command/MainCommand.kt b/src/main/kotlin/app/revanced/cli/command/MainCommand.kt index 8394a7b..58774c5 100644 --- a/src/main/kotlin/app/revanced/cli/command/MainCommand.kt +++ b/src/main/kotlin/app/revanced/cli/command/MainCommand.kt @@ -37,10 +37,10 @@ private class CLIVersionProvider : IVersionProvider { description = ["Command line application to use ReVanced"], mixinStandardHelpOptions = true, versionProvider = CLIVersionProvider::class, - subcommands = [ListPatchesCommand::class] + subcommands = [ListPatchesCommand::class, UninstallCommand::class] ) internal object MainCommand : Runnable { - val logger = DefaultCliLogger() + internal val logger = DefaultCliLogger() // @ArgGroup(exclusive = false, multiplicity = "1") lateinit var args: Args @@ -145,11 +145,12 @@ internal object MainCommand : Runnable { } } + fun main(args: Array) { + CommandLine(MainCommand).execute(*args) + } + override fun run() { val patchArgs = args.patchArgs - - if (args.packageName != null) return uninstall() - val patchingArgs = patchArgs?.patchingArgs ?: return if (!patchingArgs.inputFile.exists()) return logger.error("Input file ${patchingArgs.inputFile} does not exist.") @@ -241,18 +242,6 @@ internal object MainCommand : Runnable { logger.info(result) } - /** - * Uninstall the specified package from the specified device. - * - */ - private fun uninstall() = args.deviceSerial?.let { serial -> - if (args.mount) { - AdbManager.RootAdbManager(serial, logger) - } else { - AdbManager.UserAdbManager(serial, logger) - }.uninstall(args.packageName!!) - } ?: logger.error("No device serial specified") - private fun Patcher.filterPatchSelection(patches: PatchList) = buildList { val packageName = context.packageMetadata.packageName val packageVersion = context.packageMetadata.packageVersion @@ -326,8 +315,4 @@ internal object MainCommand : Runnable { add(patch) } } - - fun main(args: Array) { - CommandLine(MainCommand).execute(*args) - } } \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/cli/command/UninstallCommand.kt b/src/main/kotlin/app/revanced/cli/command/UninstallCommand.kt new file mode 100644 index 0000000..070adb7 --- /dev/null +++ b/src/main/kotlin/app/revanced/cli/command/UninstallCommand.kt @@ -0,0 +1,44 @@ +package app.revanced.cli.command + +import app.revanced.utils.adb.AdbManager +import picocli.CommandLine +import picocli.CommandLine.Help.Visibility.ALWAYS + + +@CommandLine.Command( + name = "uninstall", + description = ["Uninstall a patched package from the devices with the supplied ADB device serials"] +) +class UninstallCommand : Runnable { + @CommandLine.Parameters( + description = ["ADB device serials"], + arity = "1..*" + ) + lateinit var deviceSerials: Array + + @CommandLine.Option( + names = ["-p", "--package-name"], + description = ["Package name to uninstall"], + required = true + ) + lateinit var packageName: String + + @CommandLine.Option( + names = ["-u", "--unmount"], + description = ["Uninstall by unmounting the patched package"], + showDefaultValue = ALWAYS + ) + var unmount: Boolean = false + + override fun run() = try { + deviceSerials.forEach {deviceSerial -> + if (unmount) { + AdbManager.RootAdbManager(deviceSerial, MainCommand.logger) + } else { + AdbManager.UserAdbManager(deviceSerial, MainCommand.logger) + }.uninstall(packageName) + } + } catch (e: AdbManager.DeviceNotFoundException) { + MainCommand.logger.error(e.toString()) + } +} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/utils/adb/AdbManager.kt b/src/main/kotlin/app/revanced/utils/adb/AdbManager.kt index 1bbc28d..2044f51 100644 --- a/src/main/kotlin/app/revanced/utils/adb/AdbManager.kt +++ b/src/main/kotlin/app/revanced/utils/adb/AdbManager.kt @@ -27,7 +27,7 @@ import java.io.File */ internal sealed class AdbManager(deviceSerial: String? = null, protected val logger: CliLogger? = null) : Closeable { protected val device = JadbConnection().devices.find { device -> device.serial == deviceSerial } - ?: throw IllegalArgumentException("The device with the serial $deviceSerial can not be found.") + ?: throw DeviceNotFoundException(deviceSerial) init { logger?.trace("Established connection to $deviceSerial") @@ -127,4 +127,9 @@ internal sealed class AdbManager(deviceSerial: String? = null, protected val log * @param file The [Apk] file. */ internal class Apk(val file: File, val packageName: String? = null) + + internal class DeviceNotFoundException(deviceSerial: String?) : + Exception(deviceSerial?.let { + "The device with the ADB device serial \"$deviceSerial\" can not be found" + } ?: "No ADB device found") } \ No newline at end of file From 8a5daab2a36c5e865352ad27acde820934bb7d03 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Wed, 23 Aug 2023 03:06:27 +0200 Subject: [PATCH 21/62] build: add properties resource file --- build.gradle.kts | 19 +- .../cli/command/ListPatchesCommand.kt | 18 +- .../app/revanced/cli/command/MainCommand.kt | 318 ------------------ .../revanced/cli/command/UninstallCommand.kt | 18 +- .../cli/logging/impl/DefaultCliLogger.kt | 4 +- .../app/revanced/cli/signing/Signing.kt | 12 - .../revanced/cli/signing/SigningOptions.kt | 7 - .../app/revanced/cli/version.properties | 1 + 8 files changed, 31 insertions(+), 366 deletions(-) delete mode 100644 src/main/kotlin/app/revanced/cli/command/MainCommand.kt delete mode 100644 src/main/kotlin/app/revanced/cli/signing/Signing.kt delete mode 100644 src/main/kotlin/app/revanced/cli/signing/SigningOptions.kt create mode 100644 src/main/resources/app/revanced/cli/version.properties diff --git a/build.gradle.kts b/build.gradle.kts index 3fa04cb..5510647 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -24,7 +24,7 @@ repositories { dependencies { implementation("app.revanced:revanced-patcher:14.0.0") - implementation("org.jetbrains.kotlin:kotlin-reflect:1.8.22") + implementation("org.jetbrains.kotlin:kotlin-reflect:1.9.0") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1") implementation("info.picocli:picocli:4.7.3") implementation("com.github.revanced:jadb:2531a28109") // Updated fork @@ -34,9 +34,8 @@ dependencies { testImplementation("org.jetbrains.kotlin:kotlin-test:1.8.20-RC") } -kotlin { - jvmToolchain(11) -} +kotlin { jvmToolchain(11) } + tasks { test { @@ -45,9 +44,15 @@ tasks { events("PASSED", "SKIPPED", "FAILED") } } + + processResources { + expand("projectVersion" to project.version) + } + build { dependsOn(shadowJar) } + shadowJar { manifest { attributes("Main-Class" to "app.revanced.cli.main.MainKt") @@ -61,9 +66,5 @@ tasks { // Dummy task to fix the Gradle semantic-release plugin. // Remove this if you forked it to support building only. // Tracking issue: https://github.com/KengoTODA/gradle-semantic-release-plugin/issues/435 - register("publish") { - group = "publish" - description = "Dummy task" - dependsOn(build) - } + register("publish") { } } diff --git a/src/main/kotlin/app/revanced/cli/command/ListPatchesCommand.kt b/src/main/kotlin/app/revanced/cli/command/ListPatchesCommand.kt index 9c42595..931ec48 100644 --- a/src/main/kotlin/app/revanced/cli/command/ListPatchesCommand.kt +++ b/src/main/kotlin/app/revanced/cli/command/ListPatchesCommand.kt @@ -8,41 +8,41 @@ import app.revanced.patcher.extensions.PatchExtensions.options import app.revanced.patcher.extensions.PatchExtensions.patchName import app.revanced.patcher.patch.PatchClass import app.revanced.patcher.patch.PatchOption -import picocli.CommandLine +import picocli.CommandLine.* import picocli.CommandLine.Help.Visibility.ALWAYS import java.io.File -@CommandLine.Command(name = "list-patches", description = ["List patches from supplied patch bundles"]) -class ListPatchesCommand : Runnable { - @CommandLine.Parameters( +@Command(name = "list-patches", description = ["List patches from supplied patch bundles"]) +internal object ListPatchesCommand : Runnable { + @Parameters( description = ["Paths to patch bundles"], arity = "1..*" ) lateinit var patchBundles: Array - @CommandLine.Option( + @Option( names = ["-d", "--with-descriptions"], description = ["List their descriptions"], showDefaultValue = ALWAYS ) var withDescriptions: Boolean = true - @CommandLine.Option( + @Option( names = ["-p", "--with-packages"], description = ["List the packages the patches are compatible with"], showDefaultValue = ALWAYS ) var withPackages: Boolean = false - @CommandLine.Option( + @Option( names = ["-v", "--with-versions"], description = ["List the versions of the packages the patches are compatible with"], showDefaultValue = ALWAYS ) var withVersions: Boolean = false - @CommandLine.Option( + @Option( names = ["-o", "--with-options"], description = ["List the options of the patches"], showDefaultValue = ALWAYS @@ -89,6 +89,6 @@ class ListPatchesCommand : Runnable { } } - MainCommand.logger.info(PatchBundleLoader.Jar(*patchBundles).joinToString("\n\n") { it.buildString() }) + logger.info(PatchBundleLoader.Jar(*patchBundles).joinToString("\n\n") { it.buildString() }) } } \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/cli/command/MainCommand.kt b/src/main/kotlin/app/revanced/cli/command/MainCommand.kt deleted file mode 100644 index 58774c5..0000000 --- a/src/main/kotlin/app/revanced/cli/command/MainCommand.kt +++ /dev/null @@ -1,318 +0,0 @@ -package app.revanced.cli.command - -import app.revanced.cli.aligning.Aligning -import app.revanced.cli.logging.impl.DefaultCliLogger -import app.revanced.cli.patcher.logging.impl.PatcherLogger -import app.revanced.cli.signing.Signing -import app.revanced.cli.signing.SigningOptions -import app.revanced.patcher.PatchBundleLoader -import app.revanced.patcher.Patcher -import app.revanced.patcher.PatcherOptions -import app.revanced.patcher.extensions.PatchExtensions.compatiblePackages -import app.revanced.patcher.extensions.PatchExtensions.include -import app.revanced.patcher.extensions.PatchExtensions.patchName -import app.revanced.patcher.patch.PatchClass -import app.revanced.utils.Options -import app.revanced.utils.Options.setOptions -import app.revanced.utils.adb.AdbManager -import kotlinx.coroutines.runBlocking -import picocli.CommandLine -import picocli.CommandLine.* -import java.io.File - -fun main(args: Array) { - CommandLine(MainCommand).execute(*args) -} - -internal typealias PatchList = List - -private class CLIVersionProvider : IVersionProvider { - override fun getVersion() = arrayOf( - MainCommand::class.java.`package`.implementationVersion ?: "unknown" - ) -} - -@Command( - name = "ReVanced CLI", - description = ["Command line application to use ReVanced"], - mixinStandardHelpOptions = true, - versionProvider = CLIVersionProvider::class, - subcommands = [ListPatchesCommand::class, UninstallCommand::class] -) -internal object MainCommand : Runnable { - internal val logger = DefaultCliLogger() - - // @ArgGroup(exclusive = false, multiplicity = "1") - lateinit var args: Args - - /** - * Arguments for the CLI - */ - class Args { - @Option(names = ["--uninstall"], description = ["Package name to uninstall"]) - var packageName: String? = null - - @Option(names = ["-d", "--device-serial"], description = ["ADB device serial number to deploy to"]) - var deviceSerial: String? = null - - @Option(names = ["--mount"], description = ["Handle deployments by mounting"]) - var mount: Boolean = false - - @ArgGroup(exclusive = false) - var patchArgs: PatchArgs? = null - - /** - * Arguments for patches. - */ - class PatchArgs { - @Option(names = ["-b", "--bundle"], description = ["One or more bundles of patches"], required = true) - var patchBundles = emptyList() - - @ArgGroup(exclusive = false) - var patchingArgs: PatchingArgs? = null - - /** - * Arguments for patching. - */ - class PatchingArgs { - @Option(names = ["-a", "--apk"], description = ["APK file to be patched"], required = true) - lateinit var inputFile: File - - @Option( - names = ["-o", "--out"], - description = ["Path to save the patched APK file to"], - required = true - ) - lateinit var outputFilePath: File - - @Option(names = ["--options"], description = ["Path to patch options JSON file"]) - var optionsFile: File = File("options.json") - - @Option(names = ["-e", "--exclude"], description = ["List of patches to exclude"]) - var excludedPatches = arrayOf() - - @Option( - names = ["--exclusive"], - description = ["Only include patches that are explicitly specified to be included"] - ) - var exclusive = false - - @Option(names = ["-i", "--include"], description = ["List of patches to include"]) - var includedPatches = arrayOf() - - @Option(names = ["--experimental"], description = ["Ignore patches incompatibility to versions"]) - var experimental: Boolean = false - - @Option( - names = ["-m", "--merge"], - description = ["One or more DEX files or containers to merge into the APK"] - ) - var integrations = listOf() - - @Option(names = ["--cn"], description = ["The common name of the signer of the patched APK file"]) - var commonName = "ReVanced" - - @Option( - names = ["--keystore"], - description = ["Path to the keystore to sign the patched APK file with"] - ) - var keystorePath: String? = null - - @Option( - names = ["-p", "--password"], - description = ["The password of the keystore to sign the patched APK file with"] - ) - var password = "ReVanced" - - @Option( - names = ["-r", "--resource-cache"], - description = ["Path to temporary resource cache directory"] - ) - var resourceCachePath = File("revanced-resource-cache") - - @Option( - names = ["-c", "--clean"], - description = ["Clean up the temporary resource cache directory after patching"] - ) - var clean: Boolean = false - - @Option( - names = ["--custom-aapt2-binary"], - description = ["Path to a custom AAPT binary to compile resources with"] - ) - var aaptBinaryPath = File("") - } - } - } - - fun main(args: Array) { - CommandLine(MainCommand).execute(*args) - } - - override fun run() { - val patchArgs = args.patchArgs - val patchingArgs = patchArgs?.patchingArgs ?: return - - if (!patchingArgs.inputFile.exists()) return logger.error("Input file ${patchingArgs.inputFile} does not exist.") - - logger.info("Loading patches") - - val patches = PatchBundleLoader.Jar(*patchArgs.patchBundles.toTypedArray()) - val integrations = patchingArgs.integrations - - logger.info("Setting up patch options") - - patchingArgs.optionsFile.let { - if (it.exists()) patches.setOptions(it, logger) - else Options.serialize(patches, prettyPrint = true).let(it::writeText) - } - - val adbManager = args.deviceSerial?.let { serial -> - if (args.mount) AdbManager.RootAdbManager(serial, logger) else AdbManager.UserAdbManager(serial, logger) - } - - val patcher = Patcher( - PatcherOptions( - patchingArgs.inputFile, - patchingArgs.resourceCachePath, - patchingArgs.aaptBinaryPath.absolutePath, - patchingArgs.resourceCachePath.absolutePath, - PatcherLogger - ) - ) - - val result = patcher.apply { - acceptIntegrations(integrations) - acceptPatches(filterPatchSelection(patches)) - - // Execute patches. - runBlocking { - apply(false).collect { patchResult -> - patchResult.exception?.let { - logger.error("${patchResult.patchName} failed:\n${patchResult.exception}") - } ?: logger.info("${patchResult.patchName} succeeded") - } - } - }.get() - - patcher.close() - - val outputFileNameWithoutExtension = patchingArgs.outputFilePath.nameWithoutExtension - - // Align the file. - val alignedFile = patchingArgs.resourceCachePath.resolve("${outputFileNameWithoutExtension}_aligned.apk") - Aligning.align(result, patchingArgs.inputFile, alignedFile) - - // Sign the file if needed. - val finalFile = if (!args.mount) { - val signedOutput = patchingArgs.resourceCachePath.resolve("${outputFileNameWithoutExtension}_signed.apk") - Signing.sign( - alignedFile, - signedOutput, - SigningOptions( - patchingArgs.commonName, - patchingArgs.password, - patchingArgs.keystorePath ?: patchingArgs.outputFilePath.absoluteFile.parentFile - .resolve("${patchingArgs.outputFilePath.nameWithoutExtension}.keystore") - .canonicalPath - ) - ) - - signedOutput - } else - alignedFile - - logger.info("Copying ${finalFile.name} to ${patchingArgs.outputFilePath.name}") - - finalFile.copyTo(patchingArgs.outputFilePath, overwrite = true) - adbManager?.install(AdbManager.Apk(patchingArgs.outputFilePath, patcher.context.packageMetadata.packageName)) - - if (patchingArgs.clean) { - logger.info("Cleaning up temporary files") - patchingArgs.outputFilePath.delete() - cleanUp(patchingArgs.resourceCachePath) - } - } - - private fun cleanUp(resourceCachePath: File) { - val result = if (resourceCachePath.deleteRecursively()) - "Cleaned up cache directory" - else - "Failed to clean up cache directory" - logger.info(result) - } - - private fun Patcher.filterPatchSelection(patches: PatchList) = buildList { - val packageName = context.packageMetadata.packageName - val packageVersion = context.packageMetadata.packageVersion - val patchingArgs = args.patchArgs!!.patchingArgs!! - - patches.forEach patch@{ patch -> - val formattedPatchName = patch.patchName.lowercase().replace(" ", "-") - - /** - * Check if the patch is explicitly excluded. - * - * Cases: - * 1. -e patch.name - * 2. -i patch.name -e patch.name - */ - - val excluded = patchingArgs.excludedPatches.contains(formattedPatchName) - if (excluded) return@patch logger.info("Excluding ${patch.patchName}") - - /** - * Check if the patch is constrained to packages. - */ - - patch.compatiblePackages?.let { packages -> - packages.singleOrNull { it.name == packageName }?.let { `package` -> - /** - * Check if the package version matches. - * If experimental is true, version matching will be skipped. - */ - - val matchesVersion = patchingArgs.experimental || `package`.versions.let { - it.isEmpty() || it.any { version -> version == packageVersion } - } - - if (!matchesVersion) return@patch logger.warn( - "${patch.patchName} is incompatible with version $packageVersion. " + - "This patch is only compatible with version " + - packages.joinToString(";") { `package` -> - "${`package`.name}: ${`package`.versions.joinToString(", ")}" - } - ) - - } ?: return@patch logger.trace( - "${patch.patchName} is incompatible with $packageName. " + - "This patch is only compatible with " + - packages.joinToString(", ") { `package` -> `package`.name } - ) - - return@let - } ?: logger.trace("$formattedPatchName: No constraint on packages.") - - /** - * Check if the patch is explicitly included. - * - * Cases: - * 1. --exclusive - * 2. --exclusive -i patch.name - */ - - val exclusive = patchingArgs.exclusive - val explicitlyIncluded = patchingArgs.includedPatches.contains(formattedPatchName) - - val implicitlyIncluded = !exclusive && patch.include // Case 3. - val exclusivelyIncluded = exclusive && explicitlyIncluded // Case 2. - - val included = implicitlyIncluded || exclusivelyIncluded - if (!included) return@patch logger.info("${patch.patchName} excluded by default") // Case 1. - - logger.trace("Adding $formattedPatchName") - - add(patch) - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/cli/command/UninstallCommand.kt b/src/main/kotlin/app/revanced/cli/command/UninstallCommand.kt index 070adb7..27916de 100644 --- a/src/main/kotlin/app/revanced/cli/command/UninstallCommand.kt +++ b/src/main/kotlin/app/revanced/cli/command/UninstallCommand.kt @@ -1,29 +1,29 @@ package app.revanced.cli.command import app.revanced.utils.adb.AdbManager -import picocli.CommandLine +import picocli.CommandLine.* import picocli.CommandLine.Help.Visibility.ALWAYS -@CommandLine.Command( +@Command( name = "uninstall", description = ["Uninstall a patched package from the devices with the supplied ADB device serials"] ) -class UninstallCommand : Runnable { - @CommandLine.Parameters( +internal object UninstallCommand : Runnable { + @Parameters( description = ["ADB device serials"], arity = "1..*" ) lateinit var deviceSerials: Array - @CommandLine.Option( + @Option( names = ["-p", "--package-name"], description = ["Package name to uninstall"], required = true ) lateinit var packageName: String - @CommandLine.Option( + @Option( names = ["-u", "--unmount"], description = ["Uninstall by unmounting the patched package"], showDefaultValue = ALWAYS @@ -33,12 +33,12 @@ class UninstallCommand : Runnable { override fun run() = try { deviceSerials.forEach {deviceSerial -> if (unmount) { - AdbManager.RootAdbManager(deviceSerial, MainCommand.logger) + AdbManager.RootAdbManager(deviceSerial, logger) } else { - AdbManager.UserAdbManager(deviceSerial, MainCommand.logger) + AdbManager.UserAdbManager(deviceSerial, logger) }.uninstall(packageName) } } catch (e: AdbManager.DeviceNotFoundException) { - MainCommand.logger.error(e.toString()) + logger.error(e.toString()) } } \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/cli/logging/impl/DefaultCliLogger.kt b/src/main/kotlin/app/revanced/cli/logging/impl/DefaultCliLogger.kt index db9306a..d3b22bd 100644 --- a/src/main/kotlin/app/revanced/cli/logging/impl/DefaultCliLogger.kt +++ b/src/main/kotlin/app/revanced/cli/logging/impl/DefaultCliLogger.kt @@ -1,12 +1,12 @@ package app.revanced.cli.logging.impl -import app.revanced.cli.command.MainCommand +import app.revanced.cli.command.Main import app.revanced.cli.logging.CliLogger import java.util.logging.Logger import java.util.logging.SimpleFormatter internal class DefaultCliLogger( - private val logger: Logger = Logger.getLogger(MainCommand::class.java.name), + private val logger: Logger = Logger.getLogger(Main::class.java.name), private val errorLogger: Logger = Logger.getLogger(logger.name + "Err") ) : CliLogger { diff --git a/src/main/kotlin/app/revanced/cli/signing/Signing.kt b/src/main/kotlin/app/revanced/cli/signing/Signing.kt deleted file mode 100644 index 75b750c..0000000 --- a/src/main/kotlin/app/revanced/cli/signing/Signing.kt +++ /dev/null @@ -1,12 +0,0 @@ -package app.revanced.cli.signing - -import app.revanced.cli.command.MainCommand.logger -import app.revanced.utils.signing.Signer -import java.io.File - -object Signing { - fun sign(alignedFile: File, signedOutput: File, signingOptions: SigningOptions) { - logger.info("Signing ${alignedFile.name} to ${signedOutput.name}") - Signer(signingOptions).signApk(alignedFile, signedOutput) - } -} diff --git a/src/main/kotlin/app/revanced/cli/signing/SigningOptions.kt b/src/main/kotlin/app/revanced/cli/signing/SigningOptions.kt deleted file mode 100644 index 252ef65..0000000 --- a/src/main/kotlin/app/revanced/cli/signing/SigningOptions.kt +++ /dev/null @@ -1,7 +0,0 @@ -package app.revanced.cli.signing - -data class SigningOptions( - val cn: String, - val password: String, - val keyStoreFilePath: String -) \ No newline at end of file diff --git a/src/main/resources/app/revanced/cli/version.properties b/src/main/resources/app/revanced/cli/version.properties new file mode 100644 index 0000000..308c9f8 --- /dev/null +++ b/src/main/resources/app/revanced/cli/version.properties @@ -0,0 +1 @@ +version=${projectVersion} \ No newline at end of file From 32da961d57537e99b39fd92b625a1c73f8314bc6 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Wed, 23 Aug 2023 03:08:21 +0200 Subject: [PATCH 22/62] feat: use separate command to patch --- docs/0_prerequisites.md | 2 +- docs/1_usage.md | 32 +- .../app/revanced/cli/aligning/Aligning.kt | 37 -- .../kotlin/app/revanced/cli/command/Main.kt | 39 ++ .../app/revanced/cli/command/PatchCommand.kt | 412 ++++++++++++++++++ .../utils/{signing => }/align/ZipAligner.kt | 4 +- .../{signing => }/align/zip/Extensions.kt | 2 +- .../utils/{signing => }/align/zip/ZipFile.kt | 51 ++- .../align/zip/structures/ZipEndRecord.kt | 10 +- .../align/zip/structures/ZipEntry.kt | 4 +- .../utils/signing/{Signer.kt => ApkSigner.kt} | 59 ++- .../revanced/utils/signing/SigningOptions.kt | 7 + 12 files changed, 547 insertions(+), 112 deletions(-) delete mode 100644 src/main/kotlin/app/revanced/cli/aligning/Aligning.kt create mode 100644 src/main/kotlin/app/revanced/cli/command/Main.kt create mode 100644 src/main/kotlin/app/revanced/cli/command/PatchCommand.kt rename src/main/kotlin/app/revanced/utils/{signing => }/align/ZipAligner.kt (74%) rename src/main/kotlin/app/revanced/utils/{signing => }/align/zip/Extensions.kt (96%) rename src/main/kotlin/app/revanced/utils/{signing => }/align/zip/ZipFile.kt (75%) rename src/main/kotlin/app/revanced/utils/{signing => }/align/zip/structures/ZipEndRecord.kt (89%) rename src/main/kotlin/app/revanced/utils/{signing => }/align/zip/structures/ZipEntry.kt (98%) rename src/main/kotlin/app/revanced/utils/signing/{Signer.kt => ApkSigner.kt} (68%) create mode 100644 src/main/kotlin/app/revanced/utils/signing/SigningOptions.kt diff --git a/docs/0_prerequisites.md b/docs/0_prerequisites.md index d8654d8..ed5660f 100644 --- a/docs/0_prerequisites.md +++ b/docs/0_prerequisites.md @@ -5,7 +5,7 @@ To use ReVanced CLI, you will need to fulfil specific requirements. ## 🤝 Requirements - Java SDK 11 (Azul Zulu JDK or OpenJDK) -- [Android Debug Bridge (adb)](https://developer.android.com/studio/command-line/adb) if you want to deploy the patched APK file on your device +- [Android Debug Bridge (adb)](https://developer.android.com/studio/command-line/adb) if you want to install the patched APK file on your device - An ABI other than ARMv7 such as x86 or x86-64 (or a custom AAPT binary that supports ARMv7) - ReVanced Patches - ReVanced Integrations, if the patches require it diff --git a/docs/1_usage.md b/docs/1_usage.md index 932fdb2..a03abaa 100644 --- a/docs/1_usage.md +++ b/docs/1_usage.md @@ -10,7 +10,7 @@ Learn how to ReVanced CLI. adb shell exit ``` - If you want to deploy the patched APK file on your device by mounting it on top of the original APK file, you will need root access. This is optional. + If you want to install the patched APK file on your device by mounting it on top of the original APK file, you will need root access. This is optional. ```bash adb shell su -c exit @@ -33,8 +33,7 @@ Learn how to ReVanced CLI. - ### 📃 List patches from supplied patch bundles ```bash - java -jar revanced-cli.jar \ - list-patches \ + java -jar revanced-cli.jar list-patches \ --with-packages \ --with-versions \ --with-options \ @@ -49,31 +48,31 @@ Learn how to ReVanced CLI. > **Note**: The `options.json` file will be generated at the first time you use ReVanced CLI to patch an APK file for now. This will be changed in the future. -- ### 💉 Use ReVanced CLI to patch an APK file but deploy without root permissions +- ### 💉 Use ReVanced CLI to patch an APK file but install without root permissions - This will deploy the patched APK file on your device by installing it. + This will install the patched APK file regularly on your device. ```bash - java -jar revanced-cli.jar \ - -a input.apk \ - -o patched-output.apk \ + java -jar revanced-cli.jar patch \ -b revanced-patches.jar \ - -d device-serial + -o patched-output.apk \ + -d device-serial \ + input-apk ``` -- ### 👾 Use ReVanced CLI to patch an APK file but deploy with root permissions +- ### 👾 Use ReVanced CLI to patch an APK file but install with root permissions - This will deploy the patched APK file on your device by mounting it on top of the original APK file. + This will install the patched APK file on your device by mounting it on top of the original APK file. ```bash adb install input.apk - java -jar revanced-cli.jar \ - -a input.apk \ + java -jar revanced-cli.jar patch \ -o patched-output.apk \ -b revanced-patches.jar \ - -e vanced-microg-support \ + -e some-patch \ -d device-serial \ - --mount + --mount \ + input-apk ``` > **Note**: Some patches from [ReVanced Patches](https://github.com/revanced/revanced-patches) also require [ReVanced Integrations](https://github.com/revanced/revanced-integrations). Supply them with the option `-m`. ReVanced Patcher will merge ReVanced Integrations automatically, depending on if the supplied patches require them. @@ -81,8 +80,7 @@ Learn how to ReVanced CLI. - ### 🗑️ Uninstall a patched ```bash - java -jar revanced-cli.jar \ - uninstall \ + java -jar revanced-cli.jar uninstall \ -p package-name \ device-serial ``` diff --git a/src/main/kotlin/app/revanced/cli/aligning/Aligning.kt b/src/main/kotlin/app/revanced/cli/aligning/Aligning.kt deleted file mode 100644 index d67c2ad..0000000 --- a/src/main/kotlin/app/revanced/cli/aligning/Aligning.kt +++ /dev/null @@ -1,37 +0,0 @@ -package app.revanced.cli.aligning - -import app.revanced.cli.command.MainCommand.logger -import app.revanced.patcher.PatcherResult -import app.revanced.utils.signing.align.ZipAligner -import app.revanced.utils.signing.align.zip.ZipFile -import app.revanced.utils.signing.align.zip.structures.ZipEntry -import java.io.File - -object Aligning { - fun align(result: PatcherResult, inputFile: File, outputFile: File) { - logger.info("Aligning ${inputFile.name} to ${outputFile.name}") - - if (outputFile.exists()) outputFile.delete() - - ZipFile(outputFile).use { file -> - result.dexFiles.forEach { - file.addEntryCompressData( - ZipEntry.createWithName(it.name), - it.stream.readBytes() - ) - } - - result.resourceFile?.let { - file.copyEntriesFromFileAligned( - ZipFile(it), - ZipAligner::getEntryAlignment - ) - } - - file.copyEntriesFromFileAligned( - ZipFile(inputFile), - ZipAligner::getEntryAlignment - ) - } - } -} diff --git a/src/main/kotlin/app/revanced/cli/command/Main.kt b/src/main/kotlin/app/revanced/cli/command/Main.kt new file mode 100644 index 0000000..d33e412 --- /dev/null +++ b/src/main/kotlin/app/revanced/cli/command/Main.kt @@ -0,0 +1,39 @@ +package app.revanced.cli.command + +import app.revanced.cli.logging.impl.DefaultCliLogger +import app.revanced.patcher.patch.PatchClass +import picocli.CommandLine +import picocli.CommandLine.Command +import picocli.CommandLine.IVersionProvider +import java.util.* + +fun main(args: Array) { + CommandLine(Main).execute(*args) +} + +internal typealias PatchList = List + +internal val logger = DefaultCliLogger() + +object CLIVersionProvider : IVersionProvider { + override fun getVersion(): Array { + Properties().apply { + load(Main::class.java.getResourceAsStream("/app/revanced/cli/version.properties")) + }.let { + return arrayOf("ReVanced CLI v${it.getProperty("version")}") + } + } +} + +@Command( + name = "revanced-cli", + description = ["Command line application to use ReVanced"], + mixinStandardHelpOptions = true, + versionProvider = CLIVersionProvider::class, + subcommands = [ + ListPatchesCommand::class, + PatchCommand::class, + UninstallCommand::class + ] +) +internal object Main \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt b/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt new file mode 100644 index 0000000..279fce8 --- /dev/null +++ b/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt @@ -0,0 +1,412 @@ +package app.revanced.cli.command + +import app.revanced.cli.patcher.logging.impl.PatcherLogger +import app.revanced.patcher.PatchBundleLoader +import app.revanced.patcher.Patcher +import app.revanced.patcher.PatcherOptions +import app.revanced.patcher.PatcherResult +import app.revanced.patcher.extensions.PatchExtensions.compatiblePackages +import app.revanced.patcher.extensions.PatchExtensions.include +import app.revanced.patcher.extensions.PatchExtensions.patchName +import app.revanced.utils.Options +import app.revanced.utils.Options.setOptions +import app.revanced.utils.adb.AdbManager +import app.revanced.utils.align.ZipAligner +import app.revanced.utils.align.zip.ZipFile +import app.revanced.utils.align.zip.structures.ZipEntry +import app.revanced.utils.signing.ApkSigner +import app.revanced.utils.signing.SigningOptions +import kotlinx.coroutines.runBlocking +import picocli.CommandLine +import picocli.CommandLine.Help.Visibility.ALWAYS +import java.io.File + + +@CommandLine.Command( + name = "patch", + description = ["Patch the supplied APK file with the supplied patches and integrations"] +) +internal object PatchCommand: Runnable { + @CommandLine.Parameters( + description = ["APK file to be patched"], + arity = "1..1" + ) + lateinit var apk: File + + @CommandLine.Option( + names = ["-b", "--bundle"], + description = ["One or more bundles of patches"], + required = true + ) + var patchBundles = emptyList() + + @CommandLine.Option( + names = ["-m", "--merge"], + description = ["One or more DEX files or containers to merge into the APK"] + ) + var integrations = listOf() + + @CommandLine.Option( + names = ["-i", "--include"], + description = ["List of patches to include"] + ) + var includedPatches = arrayOf() + + @CommandLine.Option( + names = ["-e", "--exclude"], + description = ["List of patches to exclude"] + ) + var excludedPatches = arrayOf() + + @CommandLine.Option( + names = ["--options"], + description = ["Path to patch options JSON file"], + showDefaultValue = ALWAYS + ) + var optionsFile: File = File("options.json") + + @CommandLine.Option( + names = ["--exclusive"], + description = ["Only include patches that are explicitly specified to be included"], + showDefaultValue = ALWAYS + ) + var exclusive = false + + @CommandLine.Option( + names = ["--experimental"], + description = ["Ignore patches incompatibility to versions"], + showDefaultValue = ALWAYS + ) + var experimental: Boolean = false + + @CommandLine.Option( + names = ["-o", "--out"], + description = ["Path to save the patched APK file to"], + required = true + ) + lateinit var outputFilePath: File + + @CommandLine.Option( + names = ["-d", "--device-serial"], + description = ["ADB device serial to install to"], + showDefaultValue = ALWAYS + ) + var deviceSerial: String? = null + + @CommandLine.Option( + names = ["--mount"], + description = ["Install by mounting the patched package"], + showDefaultValue = ALWAYS + ) + var mount: Boolean = false + + @CommandLine.Option( + names = ["--common-name"], + description = ["The common name of the signer of the patched APK file"], + showDefaultValue = ALWAYS + + ) + var commonName = "ReVanced" + + @CommandLine.Option( + names = ["--keystore"], + description = ["Path to the keystore to sign the patched APK file with"] + ) + var keystorePath: String? = null + + @CommandLine.Option( + names = ["--password"], + description = ["The password of the keystore to sign the patched APK file with"] + ) + var password = "ReVanced" + + @CommandLine.Option( + names = ["-r", "--resource-cache"], + description = ["Path to temporary resource cache directory"], + showDefaultValue = ALWAYS + ) + var resourceCachePath = File("revanced-resource-cache") + + @CommandLine.Option( + names = ["--custom-aapt2-binary"], + description = ["Path to a custom AAPT binary to compile resources with"] + ) + var aaptBinaryPath = File("") + + @CommandLine.Option( + names = ["-p", "--purge"], + description = ["Purge the temporary resource cache directory after patching"], + showDefaultValue = ALWAYS + ) + var purge: Boolean = false + + override fun run() { + // region Prepare + + if (!apk.exists()) { + logger.error("Input file ${apk.name} does not exist") + return + } + + val adbManager = deviceSerial?.let { serial -> + if (mount) AdbManager.RootAdbManager(serial, logger) else AdbManager.UserAdbManager( + serial, + logger + ) + } + + // endregion + + // region Load patches + + logger.info("Loading patches") + + val patches = PatchBundleLoader.Jar(*patchBundles.toTypedArray()) + val integrations = integrations + + logger.info("Setting up patch options") + + optionsFile.let { + if (it.exists()) patches.setOptions(it, logger) + else Options.serialize(patches, prettyPrint = true).let(it::writeText) + } + + // endregion + + // region Patch + + val patcher = Patcher( + PatcherOptions( + apk, + resourceCachePath, + aaptBinaryPath.absolutePath, + resourceCachePath.absolutePath, + PatcherLogger + ) + ) + + val result = patcher.apply { + acceptIntegrations(integrations) + acceptPatches(filterPatchSelection(patches)) + + // Execute patches. + runBlocking { + apply(false).collect { patchResult -> + patchResult.exception?.let { + logger.error("${patchResult.patchName} failed:\n${patchResult.exception}") + } ?: logger.info("${patchResult.patchName} succeeded") + } + } + }.get() + + patcher.close() + + // endregion + + // region Finish + + val alignAndSignedFile = sign( + apk.newAlignedFile( + result, + resourceCachePath.resolve("${outputFilePath.nameWithoutExtension}_aligned.apk") + ) + ) + + logger.info("Copying to ${outputFilePath.name}") + alignAndSignedFile.copyTo(outputFilePath, overwrite = true) + + adbManager?.install(AdbManager.Apk(outputFilePath, patcher.context.packageMetadata.packageName)) + + if (purge) { + logger.info("Purging temporary files") + outputFilePath.delete() + purge(resourceCachePath) + } + + // endregion + } + + + /** + * Filter the patches to be added to the patcher. The filter is based on the following: + * - [includedPatches] (explicitly included) + * - [excludedPatches] (explicitly excluded) + * - [exclusive] (only include patches that are explicitly included) + * - [experimental] (ignore patches incompatibility to versions) + * - package name and version of the input APK file (if [experimental] is false) + * + * @param patches The patches to filter. + * @return The filtered patches. + */ + private fun Patcher.filterPatchSelection(patches: PatchList) = buildList { + val packageName = context.packageMetadata.packageName + val packageVersion = context.packageMetadata.packageVersion + + patches.forEach patch@{ patch -> + val formattedPatchName = patch.patchName.lowercase().replace(" ", "-") + + /** + * Check if the patch is explicitly excluded. + * + * Cases: + * 1. -e patch.name + * 2. -i patch.name -e patch.name + */ + + /** + * Check if the patch is explicitly excluded. + * + * Cases: + * 1. -e patch.name + * 2. -i patch.name -e patch.name + */ + + val excluded = excludedPatches.contains(formattedPatchName) + if (excluded) return@patch logger.info("Excluding ${patch.patchName}") + + /** + * Check if the patch is constrained to packages. + */ + + /** + * Check if the patch is constrained to packages. + */ + + patch.compatiblePackages?.let { packages -> + packages.singleOrNull { it.name == packageName }?.let { `package` -> + /** + * Check if the package version matches. + * If experimental is true, version matching will be skipped. + */ + + /** + * Check if the package version matches. + * If experimental is true, version matching will be skipped. + */ + + val matchesVersion = experimental || `package`.versions.let { + it.isEmpty() || it.any { version -> version == packageVersion } + } + + if (!matchesVersion) return@patch logger.warn( + "${patch.patchName} is incompatible with version $packageVersion. " + + "This patch is only compatible with version " + + packages.joinToString(";") { `package` -> + "${`package`.name}: ${`package`.versions.joinToString(", ")}" + } + ) + + } ?: return@patch logger.trace( + "${patch.patchName} is incompatible with $packageName. " + + "This patch is only compatible with " + + packages.joinToString(", ") { `package` -> `package`.name } + ) + + return@let + } ?: logger.trace("$formattedPatchName: No constraint on packages.") + + /** + * Check if the patch is explicitly included. + * + * Cases: + * 1. --exclusive + * 2. --exclusive -i patch.name + */ + + /** + * Check if the patch is explicitly included. + * + * Cases: + * 1. --exclusive + * 2. --exclusive -i patch.name + */ + + val explicitlyIncluded = includedPatches.contains(formattedPatchName) + + val implicitlyIncluded = !exclusive && patch.include // Case 3. + val exclusivelyIncluded = exclusive && explicitlyIncluded // Case 2. + + val included = implicitlyIncluded || exclusivelyIncluded + if (!included) return@patch logger.info("${patch.patchName} excluded by default") // Case 1. + + logger.trace("Adding $formattedPatchName") + + add(patch) + } + } + + /** + * Create a new aligned APK file. + * + * @param result The result of the patching process. + * @param outputFile The file to save the aligned APK to. + */ + private fun File.newAlignedFile( + result: PatcherResult, + outputFile: File + ): File { + logger.info("Aligning $name to ${outputFile.name}") + + if (outputFile.exists()) outputFile.delete() + + ZipFile(outputFile).use { file -> + result.dexFiles.forEach { + file.addEntryCompressData( + ZipEntry.createWithName(it.name), + it.stream.readBytes() + ) + } + + result.resourceFile?.let { + file.copyEntriesFromFileAligned( + ZipFile(it), + ZipAligner::getEntryAlignment + ) + } + + // TODO: Do not compress result.doNotCompress + + file.copyEntriesFromFileAligned( + ZipFile(this), + ZipAligner::getEntryAlignment + ) + } + + return outputFile + } + + /** + * Sign the APK file. + * + * @param inputFile The APK file to sign. + * @return The signed APK file. If [mount] is true, the input file will be returned. + */ + private fun sign(inputFile: File) = if (mount) + inputFile + else { + logger.info("Signing ${inputFile.name}") + + val keyStoreFilePath = keystorePath ?: outputFilePath + .absoluteFile.parentFile.resolve("${outputFilePath.nameWithoutExtension}.keystore").canonicalPath + + val options = SigningOptions( + commonName, + password, + keyStoreFilePath + ) + + ApkSigner(options) + .signApk( + inputFile, + resourceCachePath.resolve("${outputFilePath.nameWithoutExtension}_signed.apk") + ) + } + + private fun purge(resourceCachePath: File) { + val result = if (resourceCachePath.deleteRecursively()) + "Purged resource cache directory" + else + "Failed to purge resource cache directory" + logger.info(result) + } +} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/utils/signing/align/ZipAligner.kt b/src/main/kotlin/app/revanced/utils/align/ZipAligner.kt similarity index 74% rename from src/main/kotlin/app/revanced/utils/signing/align/ZipAligner.kt rename to src/main/kotlin/app/revanced/utils/align/ZipAligner.kt index be40ba1..568e20b 100644 --- a/src/main/kotlin/app/revanced/utils/signing/align/ZipAligner.kt +++ b/src/main/kotlin/app/revanced/utils/align/ZipAligner.kt @@ -1,6 +1,6 @@ -package app.revanced.utils.signing.align +package app.revanced.utils.align -import app.revanced.utils.signing.align.zip.structures.ZipEntry +import app.revanced.utils.align.zip.structures.ZipEntry internal object ZipAligner { private const val DEFAULT_ALIGNMENT = 4 diff --git a/src/main/kotlin/app/revanced/utils/signing/align/zip/Extensions.kt b/src/main/kotlin/app/revanced/utils/align/zip/Extensions.kt similarity index 96% rename from src/main/kotlin/app/revanced/utils/signing/align/zip/Extensions.kt rename to src/main/kotlin/app/revanced/utils/align/zip/Extensions.kt index 87f7db6..330c689 100644 --- a/src/main/kotlin/app/revanced/utils/signing/align/zip/Extensions.kt +++ b/src/main/kotlin/app/revanced/utils/align/zip/Extensions.kt @@ -1,4 +1,4 @@ -package app.revanced.utils.signing.align.zip +package app.revanced.utils.align.zip import java.io.DataInput import java.io.DataOutput diff --git a/src/main/kotlin/app/revanced/utils/signing/align/zip/ZipFile.kt b/src/main/kotlin/app/revanced/utils/align/zip/ZipFile.kt similarity index 75% rename from src/main/kotlin/app/revanced/utils/signing/align/zip/ZipFile.kt rename to src/main/kotlin/app/revanced/utils/align/zip/ZipFile.kt index e64cd1a..f961488 100644 --- a/src/main/kotlin/app/revanced/utils/signing/align/zip/ZipFile.kt +++ b/src/main/kotlin/app/revanced/utils/align/zip/ZipFile.kt @@ -1,7 +1,7 @@ -package app.revanced.utils.signing.align.zip +package app.revanced.utils.align.zip -import app.revanced.utils.signing.align.zip.structures.ZipEndRecord -import app.revanced.utils.signing.align.zip.structures.ZipEntry +import app.revanced.utils.align.zip.structures.ZipEndRecord +import app.revanced.utils.align.zip.structures.ZipEntry import java.io.Closeable import java.io.File import java.io.RandomAccessFile @@ -11,15 +11,15 @@ import java.util.zip.CRC32 import java.util.zip.Deflater class ZipFile(file: File) : Closeable { - var entries: MutableList = mutableListOf() + private var entries: MutableList = mutableListOf() private val filePointer: RandomAccessFile = RandomAccessFile(file, "rw") - private var CDNeedsRewrite = false + private var centralDirectoryNeedsRewrite = false private val compressionLevel = 5 init { - //if file isn't empty try to load entries + // If file isn't empty try to load entries. if (file.length() > 0) { val endRecord = findEndRecord() @@ -29,17 +29,17 @@ class ZipFile(file: File) : Closeable { entries = readEntries(endRecord).toMutableList() } - //seek back to start for writing + // Seek back to start for writing. filePointer.seek(0) } private fun findEndRecord(): ZipEndRecord { - //look from end to start since end record is at the end + // Look from end to start since end record is at the end. for (i in filePointer.length() - 1 downTo 0) { filePointer.seek(i) - //possible beginning of signature + // Possible beginning of signature. if (filePointer.readByte() == 0x50.toByte()) { - //seek back to get the full int + // Seek back to get the full int. filePointer.seek(i) val possibleSignature = filePointer.readUIntLE() if (possibleSignature == ZipEndRecord.ECD_SIGNATURE) { @@ -76,7 +76,7 @@ class ZipFile(file: File) : Closeable { } private fun writeCD() { - val CDStart = filePointer.channel.position().toUInt() + val centralDirectoryStartOffset = filePointer.channel.position().toUInt() entries.forEach { filePointer.channel.write(it.toCDE()) @@ -89,8 +89,8 @@ class ZipFile(file: File) : Closeable { 0u, entriesCount, entriesCount, - filePointer.channel.position().toUInt() - CDStart, - CDStart, + filePointer.channel.position().toUInt() - centralDirectoryStartOffset, + centralDirectoryStartOffset, "" ) @@ -98,7 +98,7 @@ class ZipFile(file: File) : Closeable { } private fun addEntry(entry: ZipEntry, data: ByteBuffer) { - CDNeedsRewrite = true + centralDirectoryNeedsRewrite = true entry.localHeaderOffset = filePointer.channel.position().toUInt() @@ -114,8 +114,7 @@ class ZipFile(file: File) : Closeable { compressor.finish() val uncompressedSize = data.size - val compressedData = - ByteArray(uncompressedSize) //i'm guessing compression won't make the data bigger + val compressedData = ByteArray(uncompressedSize) // I'm guessing compression won't make the data bigger. val compressedDataLength = compressor.deflate(compressedData) val compressedBuffer = @@ -126,7 +125,7 @@ class ZipFile(file: File) : Closeable { val crc = CRC32() crc.update(data) - entry.compression = 8u //deflate compression + entry.compression = 8u // Deflate compression. entry.uncompressedSize = uncompressedSize.toUInt() entry.compressedSize = compressedDataLength.toUInt() entry.crc32 = crc.value.toUInt() @@ -136,14 +135,14 @@ class ZipFile(file: File) : Closeable { private fun addEntryCopyData(entry: ZipEntry, data: ByteBuffer, alignment: Int? = null) { alignment?.let { - //calculate where data would end up + // Calculate where data would end up. val dataOffset = filePointer.filePointer + entry.LFHSize val mod = dataOffset % alignment - //wrong alignment + // Wrong alignment. if (mod != 0L) { - //add padding at end of extra field + // Add padding at end of extra field. entry.localExtraField = entry.localExtraField.copyOf((entry.localExtraField.size + (alignment - mod)).toInt()) } @@ -152,7 +151,7 @@ class ZipFile(file: File) : Closeable { addEntry(entry, data) } - fun getDataForEntry(entry: ZipEntry): ByteBuffer { + private fun getDataForEntry(entry: ZipEntry): ByteBuffer { return filePointer.channel.map( FileChannel.MapMode.READ_ONLY, entry.dataOffset.toLong(), @@ -160,9 +159,15 @@ class ZipFile(file: File) : Closeable { ) } + /** + * Copies all entries from [file] to this file but skip already existing entries. + * + * @param file The file to copy entries from. + * @param entryAlignment A function that returns the alignment for a given entry. + */ fun copyEntriesFromFileAligned(file: ZipFile, entryAlignment: (entry: ZipEntry) -> Int?) { for (entry in file.entries) { - if (entries.any { it.fileName == entry.fileName }) continue //don't add duplicates + if (entries.any { it.fileName == entry.fileName }) continue // Skip duplicates val data = file.getDataForEntry(entry) addEntryCopyData(entry, data, entryAlignment(entry)) @@ -170,7 +175,7 @@ class ZipFile(file: File) : Closeable { } override fun close() { - if (CDNeedsRewrite) writeCD() + if (centralDirectoryNeedsRewrite) writeCD() filePointer.close() } } \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/utils/signing/align/zip/structures/ZipEndRecord.kt b/src/main/kotlin/app/revanced/utils/align/zip/structures/ZipEndRecord.kt similarity index 89% rename from src/main/kotlin/app/revanced/utils/signing/align/zip/structures/ZipEndRecord.kt rename to src/main/kotlin/app/revanced/utils/align/zip/structures/ZipEndRecord.kt index d26e551..387679e 100644 --- a/src/main/kotlin/app/revanced/utils/signing/align/zip/structures/ZipEndRecord.kt +++ b/src/main/kotlin/app/revanced/utils/align/zip/structures/ZipEndRecord.kt @@ -1,9 +1,9 @@ -package app.revanced.utils.signing.align.zip.structures +package app.revanced.utils.align.zip.structures -import app.revanced.utils.signing.align.zip.putUInt -import app.revanced.utils.signing.align.zip.putUShort -import app.revanced.utils.signing.align.zip.readUIntLE -import app.revanced.utils.signing.align.zip.readUShortLE +import app.revanced.utils.align.zip.putUInt +import app.revanced.utils.align.zip.putUShort +import app.revanced.utils.align.zip.readUIntLE +import app.revanced.utils.align.zip.readUShortLE import java.io.DataInput import java.nio.ByteBuffer import java.nio.ByteOrder diff --git a/src/main/kotlin/app/revanced/utils/signing/align/zip/structures/ZipEntry.kt b/src/main/kotlin/app/revanced/utils/align/zip/structures/ZipEntry.kt similarity index 98% rename from src/main/kotlin/app/revanced/utils/signing/align/zip/structures/ZipEntry.kt rename to src/main/kotlin/app/revanced/utils/align/zip/structures/ZipEntry.kt index d99a73d..316a836 100644 --- a/src/main/kotlin/app/revanced/utils/signing/align/zip/structures/ZipEntry.kt +++ b/src/main/kotlin/app/revanced/utils/align/zip/structures/ZipEntry.kt @@ -1,6 +1,6 @@ -package app.revanced.utils.signing.align.zip.structures +package app.revanced.utils.align.zip.structures -import app.revanced.utils.signing.align.zip.* +import app.revanced.utils.align.zip.* import java.io.DataInput import java.nio.ByteBuffer import java.nio.ByteOrder diff --git a/src/main/kotlin/app/revanced/utils/signing/Signer.kt b/src/main/kotlin/app/revanced/utils/signing/ApkSigner.kt similarity index 68% rename from src/main/kotlin/app/revanced/utils/signing/Signer.kt rename to src/main/kotlin/app/revanced/utils/signing/ApkSigner.kt index 358395a..a6bf337 100644 --- a/src/main/kotlin/app/revanced/utils/signing/Signer.kt +++ b/src/main/kotlin/app/revanced/utils/signing/ApkSigner.kt @@ -1,7 +1,6 @@ package app.revanced.utils.signing -import app.revanced.cli.command.MainCommand.logger -import app.revanced.cli.signing.SigningOptions +import app.revanced.cli.command.logger import com.android.apksig.ApkSigner import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo @@ -18,10 +17,40 @@ import java.security.* import java.security.cert.X509Certificate import java.util.* -internal class Signer( +internal class ApkSigner( private val signingOptions: SigningOptions ) { + private val signer: ApkSigner.Builder private val passwordCharArray = signingOptions.password.toCharArray() + + init { + Security.addProvider(BouncyCastleProvider()) + + val keyStore = KeyStore.getInstance("BKS", "BC") + val alias = keyStore.let { store -> + FileInputStream(File(signingOptions.keyStoreFilePath).also { + if (!it.exists()) { + logger.info("Creating keystore at ${it.absolutePath}") + newKeystore(it) + } else { + logger.info("Using keystore at ${it.absolutePath}") + } + }).use { fis -> store.load(fis, null) } + store.aliases().nextElement() + } + + with( + ApkSigner.SignerConfig.Builder( + signingOptions.cn, + keyStore.getKey(alias, passwordCharArray) as PrivateKey, + listOf(keyStore.getCertificate(alias) as X509Certificate) + ).build() + ) { + this@ApkSigner.signer = ApkSigner.Builder(listOf(this)) + signer.setCreatedBy(signingOptions.cn) + } + } + private fun newKeystore(out: File) { val (publicKey, privateKey) = createKey() val privateKS = KeyStore.getInstance("BKS", "BC") @@ -50,30 +79,12 @@ internal class Signer( return JcaX509CertificateConverter().getCertificate(builder.build(signer)) to pair.private } - fun signApk(input: File, output: File) { - Security.addProvider(BouncyCastleProvider()) - - // TODO: keystore should be saved securely - val ks = File(signingOptions.keyStoreFilePath) - if (!ks.exists()) newKeystore(ks) else { - logger.info("Found existing keystore: ${ks.name}") - } - - val keyStore = KeyStore.getInstance("BKS", "BC") - FileInputStream(ks).use { fis -> keyStore.load(fis, null) } - val alias = keyStore.aliases().nextElement() - - val config = ApkSigner.SignerConfig.Builder( - signingOptions.cn, - keyStore.getKey(alias, passwordCharArray) as PrivateKey, - listOf(keyStore.getCertificate(alias) as X509Certificate) - ).build() - - val signer = ApkSigner.Builder(listOf(config)) - signer.setCreatedBy(signingOptions.cn) + fun signApk(input: File, output: File): File { signer.setInputApk(input) signer.setOutputApk(output) signer.build().sign() + + return output } } \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/utils/signing/SigningOptions.kt b/src/main/kotlin/app/revanced/utils/signing/SigningOptions.kt new file mode 100644 index 0000000..9ffdc6d --- /dev/null +++ b/src/main/kotlin/app/revanced/utils/signing/SigningOptions.kt @@ -0,0 +1,7 @@ +package app.revanced.utils.signing + +data class SigningOptions( + val cn: String, + val password: String, + val keyStoreFilePath: String +) \ No newline at end of file From 9edbbf31635603f89fc7bc5dcc6c023d4cdbb5a6 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Wed, 23 Aug 2023 03:35:38 +0200 Subject: [PATCH 23/62] feat: add options command --- docs/1_usage.md | 16 ++++-- .../kotlin/app/revanced/cli/command/Main.kt | 3 +- .../revanced/cli/command/OptionsCommand.kt | 50 +++++++++++++++++++ 3 files changed, 64 insertions(+), 5 deletions(-) create mode 100644 src/main/kotlin/app/revanced/cli/command/OptionsCommand.kt diff --git a/docs/1_usage.md b/docs/1_usage.md index a03abaa..2bde6de 100644 --- a/docs/1_usage.md +++ b/docs/1_usage.md @@ -40,13 +40,21 @@ Learn how to ReVanced CLI. revanced-patches.jar ``` -- ### ⚙️ Supply options to patches using ReVanced CLI +- ### ⚙️ Generate options from patches using ReVanced CLI - Some patches provide options. Currently, ReVanced CLI will generate and consume an `options.json` file at the location that is specified in `-o`. If the option is not specified, the options file will be generated in the current working directory. + Some patches accept options. + +- ```bash + java -jar revanced-cli.jar options \ + --overwrite \ + --update \ + revanced-patches.jar + ``` - The options file contains all options from supplied patch bundles. + > **Note**: A default `options.json` file will be automatically generated, if it does not exist + without any need of intervention. - > **Note**: The `options.json` file will be generated at the first time you use ReVanced CLI to patch an APK file for now. This will be changed in the future. + ```bash - ### 💉 Use ReVanced CLI to patch an APK file but install without root permissions diff --git a/src/main/kotlin/app/revanced/cli/command/Main.kt b/src/main/kotlin/app/revanced/cli/command/Main.kt index d33e412..e853138 100644 --- a/src/main/kotlin/app/revanced/cli/command/Main.kt +++ b/src/main/kotlin/app/revanced/cli/command/Main.kt @@ -33,7 +33,8 @@ object CLIVersionProvider : IVersionProvider { subcommands = [ ListPatchesCommand::class, PatchCommand::class, - UninstallCommand::class + UninstallCommand::class, + OptionsCommand::class, ] ) internal object Main \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/cli/command/OptionsCommand.kt b/src/main/kotlin/app/revanced/cli/command/OptionsCommand.kt new file mode 100644 index 0000000..0d0b020 --- /dev/null +++ b/src/main/kotlin/app/revanced/cli/command/OptionsCommand.kt @@ -0,0 +1,50 @@ +package app.revanced.cli.command + +import app.revanced.patcher.PatchBundleLoader +import app.revanced.utils.Options +import app.revanced.utils.Options.setOptions +import picocli.CommandLine +import picocli.CommandLine.Help.Visibility.ALWAYS +import java.io.File + +@CommandLine.Command( + name = "options", + description = ["Generate options file from patches"], +) +internal object OptionsCommand : Runnable { + @CommandLine.Parameters( + description = ["Paths to patch bundles"], + arity = "1..*" + ) + lateinit var patchBundles: Array + + @CommandLine.Option( + names = ["-p", "--path"], + description = ["Path to patch options JSON file"], + showDefaultValue = ALWAYS + ) + var path: File = File("options.json") + + @CommandLine.Option( + names = ["-o", "--overwrite"], + description = ["Overwrite existing options file"], + showDefaultValue = ALWAYS + ) + var overwrite: Boolean = false + + @CommandLine.Option( + names = ["-u", "--update"], + description = ["Update existing options by adding missing and removing non-existent options"], + showDefaultValue = ALWAYS + ) + var update: Boolean = false + + override fun run() = if (!path.exists() || overwrite) + with(PatchBundleLoader.Jar(*patchBundles)) { + if (update) setOptions(path, logger) + + Options.serialize(this, prettyPrint = true) + .let(path::writeText) + } + else logger.error("Options file already exists, use --override to override it") +} \ No newline at end of file From f8972eac3e5ee0a4a186c12cbe711925656d657b Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Wed, 23 Aug 2023 03:40:32 +0200 Subject: [PATCH 24/62] fix: use correct option name --- src/main/kotlin/app/revanced/cli/command/PatchCommand.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt b/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt index 279fce8..1298ac2 100644 --- a/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt +++ b/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt @@ -34,7 +34,7 @@ internal object PatchCommand: Runnable { lateinit var apk: File @CommandLine.Option( - names = ["-b", "--bundle"], + names = ["-b", "--patch-bundle"], description = ["One or more bundles of patches"], required = true ) From 47a20afd2de6303c4f7f39ec6d8aae4a1c95b3b6 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Wed, 23 Aug 2023 03:53:24 +0200 Subject: [PATCH 25/62] docs: improve correctness --- docs/1_usage.md | 45 ++++++++++--------- .../revanced/cli/command/UninstallCommand.kt | 2 +- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/docs/1_usage.md b/docs/1_usage.md index 2bde6de..27bd3d8 100644 --- a/docs/1_usage.md +++ b/docs/1_usage.md @@ -10,13 +10,14 @@ Learn how to ReVanced CLI. adb shell exit ``` - If you want to install the patched APK file on your device by mounting it on top of the original APK file, you will need root access. This is optional. + Optionally, you can install the patched APK file on your device by mounting it on top of the original APK file. + You will need root permissions for this. Check if you have root permissions by running the following command: ```bash adb shell su -c exit ``` -2. Get the name of your device +2. Get your device serial ```bash adb devices @@ -37,22 +38,23 @@ Learn how to ReVanced CLI. --with-packages \ --with-versions \ --with-options \ - revanced-patches.jar + revanced-patches.jar [ ...] ``` - ### ⚙️ Generate options from patches using ReVanced CLI - Some patches accept options. + This will generate an `options.json` file for the patches from a list of supplied patch bundles. + The file can be supplied to ReVanced CLI later on. - ```bash java -jar revanced-cli.jar options \ + --path options.json \ --overwrite \ - --update \ - revanced-patches.jar + revanced-patches.jar [ ...] ``` > **Note**: A default `options.json` file will be automatically generated, if it does not exist - without any need of intervention. + without any need of intervention when using the `patch` command. ```bash @@ -62,10 +64,10 @@ Learn how to ReVanced CLI. ```bash java -jar revanced-cli.jar patch \ - -b revanced-patches.jar \ - -o patched-output.apk \ - -d device-serial \ - input-apk + --patch-bundle revanced-patches.jar \ + --out output.apk \ + --device-serial \ + input.apk ``` - ### 👾 Use ReVanced CLI to patch an APK file but install with root permissions @@ -75,20 +77,23 @@ Learn how to ReVanced CLI. ```bash adb install input.apk java -jar revanced-cli.jar patch \ - -o patched-output.apk \ - -b revanced-patches.jar \ - -e some-patch \ - -d device-serial \ + --patch-bundle revanced-patches.jar \ + --include some-other-patch \ + --exclude some-patch \ + --out patched-output.apk \ + --device-serial \ --mount \ - input-apk + input.apk ``` - > **Note**: Some patches from [ReVanced Patches](https://github.com/revanced/revanced-patches) also require [ReVanced Integrations](https://github.com/revanced/revanced-integrations). Supply them with the option `-m`. ReVanced Patcher will merge ReVanced Integrations automatically, depending on if the supplied patches require them. - package + > **Note**: Some patches may require integrations + such as [ReVanced Integrations](https://github.com/revanced/revanced-integrations). + Supply them with the option `-m`. If any patches accepted by ReVanced Patcher require ReVanced Integrations, + they will be merged into the APK file automatically. - ### 🗑️ Uninstall a patched ```bash java -jar revanced-cli.jar uninstall \ - -p package-name \ - device-serial + --package-name \ + ``` diff --git a/src/main/kotlin/app/revanced/cli/command/UninstallCommand.kt b/src/main/kotlin/app/revanced/cli/command/UninstallCommand.kt index 27916de..84e673a 100644 --- a/src/main/kotlin/app/revanced/cli/command/UninstallCommand.kt +++ b/src/main/kotlin/app/revanced/cli/command/UninstallCommand.kt @@ -7,7 +7,7 @@ import picocli.CommandLine.Help.Visibility.ALWAYS @Command( name = "uninstall", - description = ["Uninstall a patched package from the devices with the supplied ADB device serials"] + description = ["Uninstall a patched APK file from the devices with the supplied ADB device serials"] ) internal object UninstallCommand : Runnable { @Parameters( From a7290353bf3def005c06e7c8997f17cf148b4d6c Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Wed, 23 Aug 2023 03:56:31 +0200 Subject: [PATCH 26/62] build: make sure to add use all necessary repositories --- build.gradle.kts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 5510647..eb96092 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,14 +12,16 @@ repositories { mavenCentral() mavenLocal() google() - maven { - url = uri("https://maven.pkg.github.com/revanced/revanced-patcher") - credentials { - username = githubUsername - password = githubPassword + maven { url = uri("https://jitpack.io") } + listOf("revanced-patcher", "jadb").forEach { repo -> + maven { + url = uri("https://maven.pkg.github.com/revanced/$repo") + credentials { + username = githubUsername + password = githubPassword + } } } - maven { url = uri("https://jitpack.io") } } dependencies { From 0dcd838de3172b160e236a588446735d8716f899 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Wed, 23 Aug 2023 04:28:12 +0200 Subject: [PATCH 27/62] build: migrate dependencies to version catalogs --- build.gradle.kts | 41 +++++++++++---------------------------- gradle/libs.versions.toml | 25 ++++++++++++++++++++++++ settings.gradle.kts | 22 +++++++++++++++++++++ 3 files changed, 58 insertions(+), 30 deletions(-) create mode 100644 gradle/libs.versions.toml diff --git a/build.gradle.kts b/build.gradle.kts index eb96092..0c40fa2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,44 +1,24 @@ plugins { kotlin("jvm") version "1.8.20" - id("com.github.johnrengelman.shadow") version "7.1.2" + alias(libs.plugins.shadow) } group = "app.revanced" -val githubUsername: String = project.findProperty("gpr.user") as? String ?: System.getenv("GITHUB_ACTOR") -val githubPassword: String = project.findProperty("gpr.key") as? String ?: System.getenv("GITHUB_TOKEN") - -repositories { - mavenCentral() - mavenLocal() - google() - maven { url = uri("https://jitpack.io") } - listOf("revanced-patcher", "jadb").forEach { repo -> - maven { - url = uri("https://maven.pkg.github.com/revanced/$repo") - credentials { - username = githubUsername - password = githubPassword - } - } - } -} - dependencies { - implementation("app.revanced:revanced-patcher:14.0.0") - implementation("org.jetbrains.kotlin:kotlin-reflect:1.9.0") - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1") - implementation("info.picocli:picocli:4.7.3") - implementation("com.github.revanced:jadb:2531a28109") // Updated fork - implementation("com.android.tools.build:apksig:8.1.0") - implementation("org.bouncycastle:bcpkix-jdk15on:1.70") - implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.14.3") - testImplementation("org.jetbrains.kotlin:kotlin-test:1.8.20-RC") + implementation(libs.revanced.patcher) + implementation(libs.kotlin.reflect) + implementation(libs.kotlinx.coroutines.core) + implementation(libs.picocli) + implementation(libs.jadb) // Updated fork + implementation(libs.apksig) + implementation(libs.bcpkix.jdk15on) + implementation(libs.jackson.module.kotlin) + testImplementation(libs.kotlin.test) } kotlin { jvmToolchain(11) } - tasks { test { useJUnitPlatform() @@ -65,6 +45,7 @@ tasks { exclude(dependency("app.revanced:.*")) } } + // Dummy task to fix the Gradle semantic-release plugin. // Remove this if you forked it to support building only. // Tracking issue: https://github.com/KengoTODA/gradle-semantic-release-plugin/issues/435 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 0000000..7b96974 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,25 @@ +[versions] +shadow = "8.1.1" +apksig = "8.1.0" +bcpkix-jdk15on = "1.70" +jackson-module-kotlin = "2.14.3" +jadb = "2531a28109" +kotlin-reflect = "1.9.0" +kotlin-test = "1.8.20-RC" +kotlinx-coroutines-core = "1.7.1" +picocli = "4.7.3" +revanced-patcher = "14.0.0" + +[libraries] +apksig = { module = "com.android.tools.build:apksig", version.ref = "apksig" } +bcpkix-jdk15on = { module = "org.bouncycastle:bcpkix-jdk15on", version.ref = "bcpkix-jdk15on" } +jackson-module-kotlin = { module = "com.fasterxml.jackson.module:jackson-module-kotlin", version.ref = "jackson-module-kotlin" } +jadb = { module = "com.github.revanced:jadb", version.ref = "jadb" } +kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin-reflect" } +kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin-test" } +kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines-core" } +picocli = { module = "info.picocli:picocli", version.ref = "picocli" } +revanced-patcher = { module = "app.revanced:revanced-patcher", version.ref = "revanced-patcher" } + +[plugins] +shadow = { id = "com.github.johnrengelman.shadow", version.ref = "shadow" } \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 028b7bc..9bd8dc7 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1 +1,23 @@ +val githubUsername: String = providers.gradleProperty("gpr.user").orNull ?: System.getenv("GITHUB_ACTOR") +val githubPassword: String = providers.gradleProperty("gpr.key").orNull ?: System.getenv("GITHUB_TOKEN") + +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + mavenCentral() + mavenLocal() + google() + maven { url = uri("https://jitpack.io") } + listOf("revanced-patcher", "jadb").forEach { repo -> + maven { + url = uri("https://maven.pkg.github.com/revanced/$repo") + credentials { + username = githubUsername + password = githubPassword + } + } + } + } +} + rootProject.name = "revanced-cli" \ No newline at end of file From a9c2a5f096627dbbf8ab1b8da26fb14529ce6bc3 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Wed, 23 Aug 2023 04:29:07 +0200 Subject: [PATCH 28/62] fix: do not use absolute path from custom AAPT2 binary option --- src/main/kotlin/app/revanced/cli/command/PatchCommand.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt b/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt index 1298ac2..14ddf82 100644 --- a/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt +++ b/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt @@ -179,7 +179,7 @@ internal object PatchCommand: Runnable { PatcherOptions( apk, resourceCachePath, - aaptBinaryPath.absolutePath, + aaptBinaryPath.path, resourceCachePath.absolutePath, PatcherLogger ) @@ -291,8 +291,8 @@ internal object PatchCommand: Runnable { if (!matchesVersion) return@patch logger.warn( "${patch.patchName} is incompatible with version $packageVersion. " + "This patch is only compatible with version " + - packages.joinToString(";") { `package` -> - "${`package`.name}: ${`package`.versions.joinToString(", ")}" + packages.joinToString(";") { pkg -> + "${pkg.name}: ${pkg.versions.joinToString(", ")}" } ) From ba758f00f4ce18791439b7e72fe1ad2e7f11f8af Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Wed, 23 Aug 2023 04:32:28 +0200 Subject: [PATCH 29/62] feat: use simpler log --- src/main/kotlin/app/revanced/cli/command/PatchCommand.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt b/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt index 14ddf82..655b26d 100644 --- a/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt +++ b/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt @@ -345,7 +345,7 @@ internal object PatchCommand: Runnable { result: PatcherResult, outputFile: File ): File { - logger.info("Aligning $name to ${outputFile.name}") + logger.info("Aligning $name") if (outputFile.exists()) outputFile.delete() From b0e748daff527ee7f417b3069882e074896fc131 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Wed, 23 Aug 2023 04:46:42 +0200 Subject: [PATCH 30/62] feat: use better logging text --- src/main/kotlin/app/revanced/cli/command/PatchCommand.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt b/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt index 655b26d..f1ccbd3 100644 --- a/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt +++ b/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt @@ -164,7 +164,7 @@ internal object PatchCommand: Runnable { val patches = PatchBundleLoader.Jar(*patchBundles.toTypedArray()) val integrations = integrations - logger.info("Setting up patch options") + logger.info("Setting patch options") optionsFile.let { if (it.exists()) patches.setOptions(it, logger) From 97548f80b33e69d58f4b753c24356b1341e5fd0b Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Wed, 23 Aug 2023 05:16:17 +0200 Subject: [PATCH 31/62] build: fix dummy publish task --- build.gradle.kts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 0c40fa2..8e661a7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -49,5 +49,8 @@ tasks { // Dummy task to fix the Gradle semantic-release plugin. // Remove this if you forked it to support building only. // Tracking issue: https://github.com/KengoTODA/gradle-semantic-release-plugin/issues/435 - register("publish") { } + register("publish") { + group = "publish" + description = "Dummy task" + } } From 44cc88b25d7887490af42c43f6b3dc9c22767d91 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 23 Aug 2023 03:17:57 +0000 Subject: [PATCH 32/62] chore(release): 3.0.0-dev.1 [skip ci] # [3.0.0-dev.1](https://github.com/ReVanced/revanced-cli/compare/v2.23.0-dev.5...v3.0.0-dev.1) (2023-08-23) ### Bug Fixes * do not use absolute path from custom AAPT2 binary option ([a9c2a5f](https://github.com/ReVanced/revanced-cli/commit/a9c2a5f096627dbbf8ab1b8da26fb14529ce6bc3)) * use correct option name ([f8972ea](https://github.com/ReVanced/revanced-cli/commit/f8972eac3e5ee0a4a186c12cbe711925656d657b)) * refactor!: restructure code ([07da528](https://github.com/ReVanced/revanced-cli/commit/07da528ce2223582f84bf64d2fec69714c647ddc)) ### Features * add options command ([9edbbf3](https://github.com/ReVanced/revanced-cli/commit/9edbbf31635603f89fc7bc5dcc6c023d4cdbb5a6)) * use better logging text ([b0e748d](https://github.com/ReVanced/revanced-cli/commit/b0e748daff527ee7f417b3069882e074896fc131)) * use separate command to list patches ([b74213f](https://github.com/ReVanced/revanced-cli/commit/b74213f66e0d04d3a0ae6197d069631388e06580)) * use separate command to patch ([32da961](https://github.com/ReVanced/revanced-cli/commit/32da961d57537e99b39fd92b625a1c73f8314bc6)) * use separate command to uninstall ([c0cc909](https://github.com/ReVanced/revanced-cli/commit/c0cc90962646cfffd5e2730ae556423271a7990b)) * use simpler log ([ba758f0](https://github.com/ReVanced/revanced-cli/commit/ba758f00f4ce18791439b7e72fe1ad2e7f11f8af)) ### BREAKING CHANGES * This introduces major changes to how ReVanced CLI is used from the command line. --- CHANGELOG.md | 26 ++++++++++++++++++++++++++ gradle.properties | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f994ac3..0c8cacf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,29 @@ +# [3.0.0-dev.1](https://github.com/ReVanced/revanced-cli/compare/v2.23.0-dev.5...v3.0.0-dev.1) (2023-08-23) + + +### Bug Fixes + +* do not use absolute path from custom AAPT2 binary option ([a9c2a5f](https://github.com/ReVanced/revanced-cli/commit/a9c2a5f096627dbbf8ab1b8da26fb14529ce6bc3)) +* use correct option name ([f8972ea](https://github.com/ReVanced/revanced-cli/commit/f8972eac3e5ee0a4a186c12cbe711925656d657b)) + + +* refactor!: restructure code ([07da528](https://github.com/ReVanced/revanced-cli/commit/07da528ce2223582f84bf64d2fec69714c647ddc)) + + +### Features + +* add options command ([9edbbf3](https://github.com/ReVanced/revanced-cli/commit/9edbbf31635603f89fc7bc5dcc6c023d4cdbb5a6)) +* use better logging text ([b0e748d](https://github.com/ReVanced/revanced-cli/commit/b0e748daff527ee7f417b3069882e074896fc131)) +* use separate command to list patches ([b74213f](https://github.com/ReVanced/revanced-cli/commit/b74213f66e0d04d3a0ae6197d069631388e06580)) +* use separate command to patch ([32da961](https://github.com/ReVanced/revanced-cli/commit/32da961d57537e99b39fd92b625a1c73f8314bc6)) +* use separate command to uninstall ([c0cc909](https://github.com/ReVanced/revanced-cli/commit/c0cc90962646cfffd5e2730ae556423271a7990b)) +* use simpler log ([ba758f0](https://github.com/ReVanced/revanced-cli/commit/ba758f00f4ce18791439b7e72fe1ad2e7f11f8af)) + + +### BREAKING CHANGES + +* This introduces major changes to how ReVanced CLI is used from the command line. + # [2.23.0-dev.5](https://github.com/ReVanced/revanced-cli/compare/v2.23.0-dev.4...v2.23.0-dev.5) (2023-08-14) # [2.23.0-dev.4](https://github.com/ReVanced/revanced-cli/compare/v2.23.0-dev.3...v2.23.0-dev.4) (2023-08-13) diff --git a/gradle.properties b/gradle.properties index 6c37885..3ed4eb0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true kotlin.code.style = official -version = 2.23.0-dev.5 +version = 3.0.0-dev.1 From 7ec9040d412a35c492137f3193e9a52ed915ff05 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Wed, 23 Aug 2023 05:37:19 +0200 Subject: [PATCH 33/62] docs: remove invalid codeblock --- docs/1_usage.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/1_usage.md b/docs/1_usage.md index 27bd3d8..380d733 100644 --- a/docs/1_usage.md +++ b/docs/1_usage.md @@ -56,8 +56,6 @@ Learn how to ReVanced CLI. > **Note**: A default `options.json` file will be automatically generated, if it does not exist without any need of intervention when using the `patch` command. - ```bash - - ### 💉 Use ReVanced CLI to patch an APK file but install without root permissions This will install the patched APK file regularly on your device. From 1fd3b83d46b1a141f49318988e41f61b733f666f Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Wed, 23 Aug 2023 05:39:31 +0200 Subject: [PATCH 34/62] docs: fix incorrect title --- docs/1_usage.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/1_usage.md b/docs/1_usage.md index 380d733..13d9170 100644 --- a/docs/1_usage.md +++ b/docs/1_usage.md @@ -17,7 +17,7 @@ Learn how to ReVanced CLI. adb shell su -c exit ``` -2. Get your device serial +2. Get your device's serial ```bash adb devices @@ -54,7 +54,7 @@ Learn how to ReVanced CLI. ``` > **Note**: A default `options.json` file will be automatically generated, if it does not exist - without any need of intervention when using the `patch` command. + without any need for intervention when using the `patch` command. - ### 💉 Use ReVanced CLI to patch an APK file but install without root permissions @@ -89,7 +89,7 @@ Learn how to ReVanced CLI. Supply them with the option `-m`. If any patches accepted by ReVanced Patcher require ReVanced Integrations, they will be merged into the APK file automatically. -- ### 🗑️ Uninstall a patched +- ### 🗑️ Uninstall a patched APK file ```bash java -jar revanced-cli.jar uninstall \ --package-name \ From 1fcc591222ab67112f2b78174a8b94106846838c Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Wed, 23 Aug 2023 14:50:32 +0200 Subject: [PATCH 35/62] fix: specify correct class containing entry-point --- build.gradle.kts | 2 +- .../app/revanced/cli/command/{Main.kt => MainCommand.kt} | 6 +++--- .../app/revanced/cli/logging/impl/DefaultCliLogger.kt | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) rename src/main/kotlin/app/revanced/cli/command/{Main.kt => MainCommand.kt} (84%) diff --git a/build.gradle.kts b/build.gradle.kts index 8e661a7..ec2f544 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -37,7 +37,7 @@ tasks { shadowJar { manifest { - attributes("Main-Class" to "app.revanced.cli.main.MainKt") + attributes("Main-Class" to "app.revanced.cli.command.MainCommandKt") } minimize { exclude(dependency("org.jetbrains.kotlin:.*")) diff --git a/src/main/kotlin/app/revanced/cli/command/Main.kt b/src/main/kotlin/app/revanced/cli/command/MainCommand.kt similarity index 84% rename from src/main/kotlin/app/revanced/cli/command/Main.kt rename to src/main/kotlin/app/revanced/cli/command/MainCommand.kt index e853138..49ae32d 100644 --- a/src/main/kotlin/app/revanced/cli/command/Main.kt +++ b/src/main/kotlin/app/revanced/cli/command/MainCommand.kt @@ -8,7 +8,7 @@ import picocli.CommandLine.IVersionProvider import java.util.* fun main(args: Array) { - CommandLine(Main).execute(*args) + CommandLine(MainCommand).execute(*args) } internal typealias PatchList = List @@ -18,7 +18,7 @@ internal val logger = DefaultCliLogger() object CLIVersionProvider : IVersionProvider { override fun getVersion(): Array { Properties().apply { - load(Main::class.java.getResourceAsStream("/app/revanced/cli/version.properties")) + load(MainCommand::class.java.getResourceAsStream("/app/revanced/cli/version.properties")) }.let { return arrayOf("ReVanced CLI v${it.getProperty("version")}") } @@ -37,4 +37,4 @@ object CLIVersionProvider : IVersionProvider { OptionsCommand::class, ] ) -internal object Main \ No newline at end of file +internal object MainCommand \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/cli/logging/impl/DefaultCliLogger.kt b/src/main/kotlin/app/revanced/cli/logging/impl/DefaultCliLogger.kt index d3b22bd..db9306a 100644 --- a/src/main/kotlin/app/revanced/cli/logging/impl/DefaultCliLogger.kt +++ b/src/main/kotlin/app/revanced/cli/logging/impl/DefaultCliLogger.kt @@ -1,12 +1,12 @@ package app.revanced.cli.logging.impl -import app.revanced.cli.command.Main +import app.revanced.cli.command.MainCommand import app.revanced.cli.logging.CliLogger import java.util.logging.Logger import java.util.logging.SimpleFormatter internal class DefaultCliLogger( - private val logger: Logger = Logger.getLogger(Main::class.java.name), + private val logger: Logger = Logger.getLogger(MainCommand::class.java.name), private val errorLogger: Logger = Logger.getLogger(logger.name + "Err") ) : CliLogger { From 1fa3dd7b8cd1a814b02d949f2ef64ca5d2154e0e Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 23 Aug 2023 12:53:55 +0000 Subject: [PATCH 36/62] chore(release): 3.0.0-dev.2 [skip ci] # [3.0.0-dev.2](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.1...v3.0.0-dev.2) (2023-08-23) ### Bug Fixes * specify correct class containing entry-point ([1fcc591](https://github.com/ReVanced/revanced-cli/commit/1fcc591222ab67112f2b78174a8b94106846838c)) --- CHANGELOG.md | 7 +++++++ gradle.properties | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c8cacf..c98f4f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [3.0.0-dev.2](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.1...v3.0.0-dev.2) (2023-08-23) + + +### Bug Fixes + +* specify correct class containing entry-point ([1fcc591](https://github.com/ReVanced/revanced-cli/commit/1fcc591222ab67112f2b78174a8b94106846838c)) + # [3.0.0-dev.1](https://github.com/ReVanced/revanced-cli/compare/v2.23.0-dev.5...v3.0.0-dev.1) (2023-08-23) diff --git a/gradle.properties b/gradle.properties index 3ed4eb0..3104ed5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true kotlin.code.style = official -version = 3.0.0-dev.1 +version = 3.0.0-dev.2 From f556909b9ea179eba465831e0db3d317b07e4994 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Wed, 23 Aug 2023 16:28:38 +0200 Subject: [PATCH 37/62] build(Needs bump): run build task when dummy publication task is executed This fixes issues with semantic-release not being able to upload release assets --- .github/workflows/release.yml | 2 +- build.gradle.kts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index afb223c..af8c277 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -36,7 +36,7 @@ jobs: - name: Build with Gradle env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: ./gradlew clean --no-daemon + run: ./gradlew build - name: Setup semantic-release run: npm install - name: Release diff --git a/build.gradle.kts b/build.gradle.kts index ec2f544..0542237 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -31,10 +31,6 @@ tasks { expand("projectVersion" to project.version) } - build { - dependsOn(shadowJar) - } - shadowJar { manifest { attributes("Main-Class" to "app.revanced.cli.command.MainCommandKt") @@ -46,6 +42,10 @@ tasks { } } + build { + dependsOn(shadowJar) + } + // Dummy task to fix the Gradle semantic-release plugin. // Remove this if you forked it to support building only. // Tracking issue: https://github.com/KengoTODA/gradle-semantic-release-plugin/issues/435 From c2da04a834c74becc8a4a58c1e241ab2088c4a74 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 23 Aug 2023 14:40:41 +0000 Subject: [PATCH 38/62] chore(release): 3.0.0-dev.3 [skip ci] # [3.0.0-dev.3](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.2...v3.0.0-dev.3) (2023-08-23) --- CHANGELOG.md | 2 ++ gradle.properties | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c98f4f5..58ec373 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +# [3.0.0-dev.3](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.2...v3.0.0-dev.3) (2023-08-23) + # [3.0.0-dev.2](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.1...v3.0.0-dev.2) (2023-08-23) diff --git a/gradle.properties b/gradle.properties index 3104ed5..0dc6130 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true kotlin.code.style = official -version = 3.0.0-dev.2 +version = 3.0.0-dev.3 From 45dd15f6796b4249e64e732d93d60fcd7033f85a Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Wed, 23 Aug 2023 16:43:15 +0200 Subject: [PATCH 39/62] build: do not use a daemon when building This decreases build times --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index af8c277..19923b4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -36,7 +36,7 @@ jobs: - name: Build with Gradle env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: ./gradlew build + run: ./gradlew build --no-daemon - name: Setup semantic-release run: npm install - name: Release From 41898d7547690e3130372414515c5645e5dc2634 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Thu, 24 Aug 2023 04:41:44 +0200 Subject: [PATCH 40/62] feat: properly make use of logging facade --- .../cli/command/ListPatchesCommand.kt | 3 ++ .../app/revanced/cli/command/MainCommand.kt | 33 +++++++++++++++++-- .../revanced/cli/command/OptionsCommand.kt | 7 ++-- .../app/revanced/cli/command/PatchCommand.kt | 25 +++++++------- .../revanced/cli/command/UninstallCommand.kt | 9 +++-- .../app/revanced/cli/logging/CliLogger.kt | 8 ----- .../cli/logging/impl/DefaultCliLogger.kt | 30 ----------------- .../cli/logging/impl/FlushingStreamHandler.kt | 13 -------- .../cli/patcher/logging/impl/PatcherLogger.kt | 13 -------- src/main/kotlin/app/revanced/utils/Options.kt | 14 ++++---- .../app/revanced/utils/adb/AdbManager.kt | 24 +++++++------- .../app/revanced/utils/signing/ApkSigner.kt | 4 ++- 12 files changed, 79 insertions(+), 104 deletions(-) delete mode 100644 src/main/kotlin/app/revanced/cli/logging/CliLogger.kt delete mode 100644 src/main/kotlin/app/revanced/cli/logging/impl/DefaultCliLogger.kt delete mode 100644 src/main/kotlin/app/revanced/cli/logging/impl/FlushingStreamHandler.kt delete mode 100644 src/main/kotlin/app/revanced/cli/patcher/logging/impl/PatcherLogger.kt diff --git a/src/main/kotlin/app/revanced/cli/command/ListPatchesCommand.kt b/src/main/kotlin/app/revanced/cli/command/ListPatchesCommand.kt index 931ec48..4ea13d2 100644 --- a/src/main/kotlin/app/revanced/cli/command/ListPatchesCommand.kt +++ b/src/main/kotlin/app/revanced/cli/command/ListPatchesCommand.kt @@ -11,10 +11,13 @@ import app.revanced.patcher.patch.PatchOption import picocli.CommandLine.* import picocli.CommandLine.Help.Visibility.ALWAYS import java.io.File +import java.util.logging.Logger @Command(name = "list-patches", description = ["List patches from supplied patch bundles"]) internal object ListPatchesCommand : Runnable { + private val logger = Logger.getLogger(ListPatchesCommand::class.java.name) + @Parameters( description = ["Paths to patch bundles"], arity = "1..*" diff --git a/src/main/kotlin/app/revanced/cli/command/MainCommand.kt b/src/main/kotlin/app/revanced/cli/command/MainCommand.kt index 49ae32d..20c6a47 100644 --- a/src/main/kotlin/app/revanced/cli/command/MainCommand.kt +++ b/src/main/kotlin/app/revanced/cli/command/MainCommand.kt @@ -1,20 +1,47 @@ package app.revanced.cli.command -import app.revanced.cli.logging.impl.DefaultCliLogger import app.revanced.patcher.patch.PatchClass import picocli.CommandLine import picocli.CommandLine.Command import picocli.CommandLine.IVersionProvider import java.util.* +import java.util.logging.* + fun main(args: Array) { + System.setProperty("java.util.logging.SimpleFormatter.format", "%4\$s: %5\$s %n") + Logger.getLogger("").apply { + handlers.forEach { + it.close() + removeHandler(it) + } + + object : Handler() { + override fun publish(record: LogRecord) = formatter.format(record).let { + if (record.level.intValue() > Level.INFO.intValue()) { + System.err.write(it.toByteArray()) + } else { + System.out.write(it.toByteArray()) + } + } + + override fun flush() { + System.out.flush() + System.err.flush() + } + + override fun close() = flush() + }.also { + it.level = Level.ALL + it.formatter = SimpleFormatter() + }.let(::addHandler) + } + CommandLine(MainCommand).execute(*args) } internal typealias PatchList = List -internal val logger = DefaultCliLogger() - object CLIVersionProvider : IVersionProvider { override fun getVersion(): Array { Properties().apply { diff --git a/src/main/kotlin/app/revanced/cli/command/OptionsCommand.kt b/src/main/kotlin/app/revanced/cli/command/OptionsCommand.kt index 0d0b020..ed623d6 100644 --- a/src/main/kotlin/app/revanced/cli/command/OptionsCommand.kt +++ b/src/main/kotlin/app/revanced/cli/command/OptionsCommand.kt @@ -6,12 +6,15 @@ import app.revanced.utils.Options.setOptions import picocli.CommandLine import picocli.CommandLine.Help.Visibility.ALWAYS import java.io.File +import java.util.logging.Logger @CommandLine.Command( name = "options", description = ["Generate options file from patches"], ) internal object OptionsCommand : Runnable { + private val logger = Logger.getLogger(OptionsCommand::class.java.name) + @CommandLine.Parameters( description = ["Paths to patch bundles"], arity = "1..*" @@ -41,10 +44,10 @@ internal object OptionsCommand : Runnable { override fun run() = if (!path.exists() || overwrite) with(PatchBundleLoader.Jar(*patchBundles)) { - if (update) setOptions(path, logger) + if (update) setOptions(path) Options.serialize(this, prettyPrint = true) .let(path::writeText) } - else logger.error("Options file already exists, use --override to override it") + else logger.severe("Options file already exists, use --override to override it") } \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt b/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt index f1ccbd3..3b6ce68 100644 --- a/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt +++ b/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt @@ -1,6 +1,5 @@ package app.revanced.cli.command -import app.revanced.cli.patcher.logging.impl.PatcherLogger import app.revanced.patcher.PatchBundleLoader import app.revanced.patcher.Patcher import app.revanced.patcher.PatcherOptions @@ -20,6 +19,7 @@ import kotlinx.coroutines.runBlocking import picocli.CommandLine import picocli.CommandLine.Help.Visibility.ALWAYS import java.io.File +import java.util.logging.Logger @CommandLine.Command( @@ -27,6 +27,8 @@ import java.io.File description = ["Patch the supplied APK file with the supplied patches and integrations"] ) internal object PatchCommand: Runnable { + private val logger = Logger.getLogger(PatchCommand::class.java.name) + @CommandLine.Parameters( description = ["APK file to be patched"], arity = "1..1" @@ -144,15 +146,13 @@ internal object PatchCommand: Runnable { // region Prepare if (!apk.exists()) { - logger.error("Input file ${apk.name} does not exist") + logger.severe("Input file ${apk.name} does not exist") return } val adbManager = deviceSerial?.let { serial -> - if (mount) AdbManager.RootAdbManager(serial, logger) else AdbManager.UserAdbManager( - serial, - logger - ) + if (mount) AdbManager.RootAdbManager(serial) + else AdbManager.UserAdbManager(serial) } // endregion @@ -167,7 +167,7 @@ internal object PatchCommand: Runnable { logger.info("Setting patch options") optionsFile.let { - if (it.exists()) patches.setOptions(it, logger) + if (it.exists()) patches.setOptions(it) else Options.serialize(patches, prettyPrint = true).let(it::writeText) } @@ -181,7 +181,6 @@ internal object PatchCommand: Runnable { resourceCachePath, aaptBinaryPath.path, resourceCachePath.absolutePath, - PatcherLogger ) ) @@ -193,7 +192,7 @@ internal object PatchCommand: Runnable { runBlocking { apply(false).collect { patchResult -> patchResult.exception?.let { - logger.error("${patchResult.patchName} failed:\n${patchResult.exception}") + logger.severe("${patchResult.patchName} failed:\n${patchResult.exception}") } ?: logger.info("${patchResult.patchName} succeeded") } } @@ -288,7 +287,7 @@ internal object PatchCommand: Runnable { it.isEmpty() || it.any { version -> version == packageVersion } } - if (!matchesVersion) return@patch logger.warn( + if (!matchesVersion) return@patch logger.warning( "${patch.patchName} is incompatible with version $packageVersion. " + "This patch is only compatible with version " + packages.joinToString(";") { pkg -> @@ -296,14 +295,14 @@ internal object PatchCommand: Runnable { } ) - } ?: return@patch logger.trace( + } ?: return@patch logger.fine( "${patch.patchName} is incompatible with $packageName. " + "This patch is only compatible with " + packages.joinToString(", ") { `package` -> `package`.name } ) return@let - } ?: logger.trace("$formattedPatchName: No constraint on packages.") + } ?: logger.fine("$formattedPatchName: No constraint on packages.") /** * Check if the patch is explicitly included. @@ -329,7 +328,7 @@ internal object PatchCommand: Runnable { val included = implicitlyIncluded || exclusivelyIncluded if (!included) return@patch logger.info("${patch.patchName} excluded by default") // Case 1. - logger.trace("Adding $formattedPatchName") + logger.fine("Adding $formattedPatchName") add(patch) } diff --git a/src/main/kotlin/app/revanced/cli/command/UninstallCommand.kt b/src/main/kotlin/app/revanced/cli/command/UninstallCommand.kt index 84e673a..a0f7f8d 100644 --- a/src/main/kotlin/app/revanced/cli/command/UninstallCommand.kt +++ b/src/main/kotlin/app/revanced/cli/command/UninstallCommand.kt @@ -3,6 +3,7 @@ package app.revanced.cli.command import app.revanced.utils.adb.AdbManager import picocli.CommandLine.* import picocli.CommandLine.Help.Visibility.ALWAYS +import java.util.logging.Logger @Command( @@ -10,6 +11,8 @@ import picocli.CommandLine.Help.Visibility.ALWAYS description = ["Uninstall a patched APK file from the devices with the supplied ADB device serials"] ) internal object UninstallCommand : Runnable { + private val logger = Logger.getLogger(UninstallCommand::class.java.name) + @Parameters( description = ["ADB device serials"], arity = "1..*" @@ -33,12 +36,12 @@ internal object UninstallCommand : Runnable { override fun run() = try { deviceSerials.forEach {deviceSerial -> if (unmount) { - AdbManager.RootAdbManager(deviceSerial, logger) + AdbManager.RootAdbManager(deviceSerial) } else { - AdbManager.UserAdbManager(deviceSerial, logger) + AdbManager.UserAdbManager(deviceSerial) }.uninstall(packageName) } } catch (e: AdbManager.DeviceNotFoundException) { - logger.error(e.toString()) + logger.severe(e.toString()) } } \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/cli/logging/CliLogger.kt b/src/main/kotlin/app/revanced/cli/logging/CliLogger.kt deleted file mode 100644 index 06849b1..0000000 --- a/src/main/kotlin/app/revanced/cli/logging/CliLogger.kt +++ /dev/null @@ -1,8 +0,0 @@ -package app.revanced.cli.logging - -internal interface CliLogger { - fun error(msg: String) - fun info(msg: String) - fun trace(msg: String) - fun warn(msg: String) -} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/cli/logging/impl/DefaultCliLogger.kt b/src/main/kotlin/app/revanced/cli/logging/impl/DefaultCliLogger.kt deleted file mode 100644 index db9306a..0000000 --- a/src/main/kotlin/app/revanced/cli/logging/impl/DefaultCliLogger.kt +++ /dev/null @@ -1,30 +0,0 @@ -package app.revanced.cli.logging.impl - -import app.revanced.cli.command.MainCommand -import app.revanced.cli.logging.CliLogger -import java.util.logging.Logger -import java.util.logging.SimpleFormatter - -internal class DefaultCliLogger( - private val logger: Logger = Logger.getLogger(MainCommand::class.java.name), - private val errorLogger: Logger = Logger.getLogger(logger.name + "Err") -) : CliLogger { - - init { - logger.useParentHandlers = false - if (logger.handlers.isEmpty()) { - logger.addHandler(FlushingStreamHandler(System.out, SimpleFormatter())) - } - } - - companion object { - init { - System.setProperty("java.util.logging.SimpleFormatter.format", "%4\$s: %5\$s %n") - } - } - - override fun error(msg: String) = errorLogger.severe(msg) - override fun info(msg: String) = logger.info(msg) - override fun trace(msg: String) = logger.finest(msg) - override fun warn(msg: String) = errorLogger.warning(msg) -} diff --git a/src/main/kotlin/app/revanced/cli/logging/impl/FlushingStreamHandler.kt b/src/main/kotlin/app/revanced/cli/logging/impl/FlushingStreamHandler.kt deleted file mode 100644 index 4bfb394..0000000 --- a/src/main/kotlin/app/revanced/cli/logging/impl/FlushingStreamHandler.kt +++ /dev/null @@ -1,13 +0,0 @@ -package app.revanced.cli.logging.impl - -import java.io.OutputStream -import java.util.logging.Formatter -import java.util.logging.LogRecord -import java.util.logging.StreamHandler - -internal class FlushingStreamHandler(out: OutputStream, format: Formatter) : StreamHandler(out, format) { - override fun publish(record: LogRecord) { - super.publish(record) - flush() - } -} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/cli/patcher/logging/impl/PatcherLogger.kt b/src/main/kotlin/app/revanced/cli/patcher/logging/impl/PatcherLogger.kt deleted file mode 100644 index 11c04a7..0000000 --- a/src/main/kotlin/app/revanced/cli/patcher/logging/impl/PatcherLogger.kt +++ /dev/null @@ -1,13 +0,0 @@ -package app.revanced.cli.patcher.logging.impl - -import app.revanced.cli.logging.impl.DefaultCliLogger -import java.util.logging.Logger - -internal object PatcherLogger : app.revanced.patcher.logging.Logger{ - private val logger = DefaultCliLogger(Logger.getLogger(app.revanced.patcher.Patcher::class.java.name)) - - override fun error(msg: String) = logger.error(msg) - override fun info(msg: String) = logger.info(msg) - override fun warn(msg: String)= logger.warn(msg) - override fun trace(msg: String)= logger.trace(msg) -} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/utils/Options.kt b/src/main/kotlin/app/revanced/utils/Options.kt index 4e07d85..534e32b 100644 --- a/src/main/kotlin/app/revanced/utils/Options.kt +++ b/src/main/kotlin/app/revanced/utils/Options.kt @@ -1,16 +1,18 @@ package app.revanced.utils import app.revanced.cli.command.PatchList -import app.revanced.cli.logging.CliLogger import app.revanced.patcher.extensions.PatchExtensions.options import app.revanced.patcher.extensions.PatchExtensions.patchName import app.revanced.patcher.patch.NoSuchOptionException import app.revanced.utils.Options.PatchOption.Option import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import java.io.File +import java.util.logging.Logger internal object Options { + private val logger = Logger.getLogger(Options::class.java.name) + private var mapper = jacksonObjectMapper() /** @@ -53,9 +55,8 @@ internal object Options { * Sets the options for the patches in the list. * * @param json The JSON string containing the options. - * @param logger The logger to use for logging. */ - fun PatchList.setOptions(json: String, logger: CliLogger? = null) { + fun PatchList.setOptions(json: String) { filter { it.options?.any() == true }.let { patches -> if (patches.isEmpty()) return @@ -66,9 +67,9 @@ internal object Options { it.options.forEach { option -> try { patch.options?.set(option.key, option.value) - ?: logger?.warn("${patch.patchName} has no options") + ?: logger.warning("${patch.patchName} has no options") } catch (e: NoSuchOptionException) { - logger?.error(e.message ?: "Unknown error") + logger.info(e.message ?: "Unknown error") } } } @@ -80,10 +81,9 @@ internal object Options { * Sets the options for the patches in the list. * * @param file The file containing the JSON string containing the options. - * @param logger The logger to use for logging. * @see setOptions */ - fun PatchList.setOptions(file: File, logger: CliLogger? = null) = setOptions(file.readText(), logger) + fun PatchList.setOptions(file: File) = setOptions(file.readText()) /** * Data class for a patch and its [Option]s. diff --git a/src/main/kotlin/app/revanced/utils/adb/AdbManager.kt b/src/main/kotlin/app/revanced/utils/adb/AdbManager.kt index 2044f51..b54249a 100644 --- a/src/main/kotlin/app/revanced/utils/adb/AdbManager.kt +++ b/src/main/kotlin/app/revanced/utils/adb/AdbManager.kt @@ -1,6 +1,5 @@ package app.revanced.utils.adb -import app.revanced.cli.logging.CliLogger import app.revanced.utils.adb.AdbManager.Apk import app.revanced.utils.adb.Constants.COMMAND_CREATE_DIR import app.revanced.utils.adb.Constants.COMMAND_DELETE @@ -19,18 +18,21 @@ import se.vidstige.jadb.managers.Package import se.vidstige.jadb.managers.PackageManager import java.io.Closeable import java.io.File +import java.util.logging.Logger /** * Adb manager. Used to install and uninstall [Apk] files. * * @param deviceSerial The serial of the device. */ -internal sealed class AdbManager(deviceSerial: String? = null, protected val logger: CliLogger? = null) : Closeable { +internal sealed class AdbManager(deviceSerial: String? = null) : Closeable { + protected val logger: Logger = Logger.getLogger(AdbManager::class.java.name) + protected val device = JadbConnection().devices.find { device -> device.serial == deviceSerial } ?: throw DeviceNotFoundException(deviceSerial) init { - logger?.trace("Established connection to $deviceSerial") + logger.fine("Established connection to $deviceSerial") } /** @@ -39,7 +41,7 @@ internal sealed class AdbManager(deviceSerial: String? = null, protected val log * @param apk The [Apk] file. */ open fun install(apk: Apk) { - logger?.info("Finished installing ${apk.file.name}") + logger.info("Finished installing ${apk.file.name}") } /** @@ -48,23 +50,23 @@ internal sealed class AdbManager(deviceSerial: String? = null, protected val log * @param packageName The package name. */ open fun uninstall(packageName: String) { - logger?.info("Finished uninstalling $packageName") + logger.info("Finished uninstalling $packageName") } /** * Closes the [AdbManager] instance. */ override fun close() { - logger?.trace("Closed") + logger.fine("Closed") } - class RootAdbManager(deviceSerial: String, logger: CliLogger? = null) : AdbManager(deviceSerial, logger) { + class RootAdbManager(deviceSerial: String) : AdbManager(deviceSerial) { init { if (!device.hasSu()) throw IllegalArgumentException("Root required on $deviceSerial. Task failed") } override fun install(apk: Apk) { - logger?.info("Installing by mounting") + logger.info("Installing by mounting") val applyReplacement = getPlaceholderReplacement( apk.packageName ?: throw IllegalArgumentException("Package name is required") @@ -86,7 +88,7 @@ internal sealed class AdbManager(deviceSerial: String? = null, protected val log } override fun uninstall(packageName: String) { - logger?.info("Uninstalling $packageName by unmounting and deleting the package") + logger.info("Uninstalling $packageName by unmounting and deleting the package") val applyReplacement = getPlaceholderReplacement(packageName) @@ -103,7 +105,7 @@ internal sealed class AdbManager(deviceSerial: String? = null, protected val log } } - class UserAdbManager(deviceSerial: String, logger: CliLogger? = null) : AdbManager(deviceSerial, logger) { + class UserAdbManager(deviceSerial: String) : AdbManager(deviceSerial) { private val packageManager = PackageManager(device) override fun install(apk: Apk) { @@ -113,7 +115,7 @@ internal sealed class AdbManager(deviceSerial: String? = null, protected val log } override fun uninstall(packageName: String) { - logger?.info("Uninstalling $packageName") + logger.info("Uninstalling $packageName") packageManager.uninstall(Package(packageName)) diff --git a/src/main/kotlin/app/revanced/utils/signing/ApkSigner.kt b/src/main/kotlin/app/revanced/utils/signing/ApkSigner.kt index a6bf337..f864490 100644 --- a/src/main/kotlin/app/revanced/utils/signing/ApkSigner.kt +++ b/src/main/kotlin/app/revanced/utils/signing/ApkSigner.kt @@ -1,6 +1,5 @@ package app.revanced.utils.signing -import app.revanced.cli.command.logger import com.android.apksig.ApkSigner import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo @@ -16,10 +15,13 @@ import java.math.BigInteger import java.security.* import java.security.cert.X509Certificate import java.util.* +import java.util.logging.Logger internal class ApkSigner( private val signingOptions: SigningOptions ) { + private val logger = Logger.getLogger(ApkSigner::class.java.name) + private val signer: ApkSigner.Builder private val passwordCharArray = signingOptions.password.toCharArray() From 7dcf15e03bf7558dcc6c34576a64c76fa6a4fda9 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Thu, 24 Aug 2023 04:52:53 +0200 Subject: [PATCH 41/62] build: bump dependencies --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7b96974..bae08e2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,7 +8,7 @@ kotlin-reflect = "1.9.0" kotlin-test = "1.8.20-RC" kotlinx-coroutines-core = "1.7.1" picocli = "4.7.3" -revanced-patcher = "14.0.0" +revanced-patcher = "14.1.0" [libraries] apksig = { module = "com.android.tools.build:apksig", version.ref = "apksig" } From 93f338a731590e7a6b8b8b55be8e078534bdee58 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 24 Aug 2023 02:56:08 +0000 Subject: [PATCH 42/62] chore(release): 3.0.0-dev.4 [skip ci] # [3.0.0-dev.4](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.3...v3.0.0-dev.4) (2023-08-24) ### Features * properly make use of logging facade ([41898d7](https://github.com/ReVanced/revanced-cli/commit/41898d7547690e3130372414515c5645e5dc2634)) --- CHANGELOG.md | 7 +++++++ gradle.properties | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58ec373..3125265 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [3.0.0-dev.4](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.3...v3.0.0-dev.4) (2023-08-24) + + +### Features + +* properly make use of logging facade ([41898d7](https://github.com/ReVanced/revanced-cli/commit/41898d7547690e3130372414515c5645e5dc2634)) + # [3.0.0-dev.3](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.2...v3.0.0-dev.3) (2023-08-23) # [3.0.0-dev.2](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.1...v3.0.0-dev.2) (2023-08-23) diff --git a/gradle.properties b/gradle.properties index 0dc6130..5121968 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true kotlin.code.style = official -version = 3.0.0-dev.3 +version = 3.0.0-dev.4 From 963ae3a5faaf776953dc15c3c3e8c42601ab4af9 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Thu, 24 Aug 2023 04:55:46 +0200 Subject: [PATCH 43/62] docs: add missing inline docs --- src/main/kotlin/app/revanced/utils/adb/AdbManager.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/kotlin/app/revanced/utils/adb/AdbManager.kt b/src/main/kotlin/app/revanced/utils/adb/AdbManager.kt index b54249a..78aa308 100644 --- a/src/main/kotlin/app/revanced/utils/adb/AdbManager.kt +++ b/src/main/kotlin/app/revanced/utils/adb/AdbManager.kt @@ -127,6 +127,7 @@ internal sealed class AdbManager(deviceSerial: String? = null) : Closeable { * Apk file for [AdbManager]. * * @param file The [Apk] file. + * @param packageName The package name of the [Apk] file. */ internal class Apk(val file: File, val packageName: String? = null) From a5851f0c1ab7196a145e647cc89486d1e216064f Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Thu, 24 Aug 2023 15:35:21 +0200 Subject: [PATCH 44/62] build: clean after building --- .github/workflows/release.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 19923b4..0861dc6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -33,10 +33,11 @@ jobs: build node_modules key: ${{ runner.os }}-gradle-npm-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'package-lock.json') }} - - name: Build with Gradle + - name: Build env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: ./gradlew build --no-daemon + # Cleaning is necessary to avoid uploading two identical artifacts with different versions + run: ./gradlew clean --no-daemon - name: Setup semantic-release run: npm install - name: Release From 11c3a6cfd4fe59ba5d703358634a1853e1cc22a5 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Thu, 24 Aug 2023 15:52:12 +0200 Subject: [PATCH 45/62] fix: only check once for patch options This prevents checking for the same patches options multiple times when it is already determined to not have any options --- src/main/kotlin/app/revanced/utils/Options.kt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/app/revanced/utils/Options.kt b/src/main/kotlin/app/revanced/utils/Options.kt index 534e32b..408e203 100644 --- a/src/main/kotlin/app/revanced/utils/Options.kt +++ b/src/main/kotlin/app/revanced/utils/Options.kt @@ -62,14 +62,17 @@ internal object Options { val patchOptions = deserialize(json) - patches.forEach { patch -> + patches.forEach patch@{ patch -> patchOptions.find { option -> option.patchName == patch.patchName }?.let { it.options.forEach { option -> try { patch.options?.set(option.key, option.value) - ?: logger.warning("${patch.patchName} has no options") + ?: run{ + logger.warning("${patch.patchName} has no options") + return@patch + } } catch (e: NoSuchOptionException) { - logger.info(e.message ?: "Unknown error") + logger.info(e.toString()) } } } From a536c9f815a6c5631f4a1edf3ce925734cc02fac Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Thu, 24 Aug 2023 15:59:44 +0200 Subject: [PATCH 46/62] refactor: use better identifiers --- .../app/revanced/utils/adb/AdbManager.kt | 44 +++++++++---------- .../kotlin/app/revanced/utils/adb/Commands.kt | 4 +- .../app/revanced/utils/adb/Constants.kt | 28 ++++++------ 3 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/main/kotlin/app/revanced/utils/adb/AdbManager.kt b/src/main/kotlin/app/revanced/utils/adb/AdbManager.kt index 78aa308..70e456e 100644 --- a/src/main/kotlin/app/revanced/utils/adb/AdbManager.kt +++ b/src/main/kotlin/app/revanced/utils/adb/AdbManager.kt @@ -1,18 +1,18 @@ package app.revanced.utils.adb import app.revanced.utils.adb.AdbManager.Apk -import app.revanced.utils.adb.Constants.COMMAND_CREATE_DIR -import app.revanced.utils.adb.Constants.COMMAND_DELETE -import app.revanced.utils.adb.Constants.COMMAND_INSTALL_MOUNT -import app.revanced.utils.adb.Constants.COMMAND_PREPARE_MOUNT_APK -import app.revanced.utils.adb.Constants.COMMAND_RESTART -import app.revanced.utils.adb.Constants.COMMAND_UMOUNT -import app.revanced.utils.adb.Constants.CONTENT_MOUNT_SCRIPT -import app.revanced.utils.adb.Constants.PATH_INIT_PUSH -import app.revanced.utils.adb.Constants.PATH_INSTALLATION -import app.revanced.utils.adb.Constants.PATH_MOUNT -import app.revanced.utils.adb.Constants.PATH_PATCHED_APK +import app.revanced.utils.adb.Constants.CREATE_DIR +import app.revanced.utils.adb.Constants.DELETE +import app.revanced.utils.adb.Constants.INSTALLATION_PATH +import app.revanced.utils.adb.Constants.INSTALL_MOUNT +import app.revanced.utils.adb.Constants.INSTALL_PATCHED_APK +import app.revanced.utils.adb.Constants.MOUNT_PATH +import app.revanced.utils.adb.Constants.MOUNT_SCRIPT +import app.revanced.utils.adb.Constants.PATCHED_APK_PATH import app.revanced.utils.adb.Constants.PLACEHOLDER +import app.revanced.utils.adb.Constants.RESTART +import app.revanced.utils.adb.Constants.TMP_PATH +import app.revanced.utils.adb.Constants.UMOUNT import se.vidstige.jadb.JadbConnection import se.vidstige.jadb.managers.Package import se.vidstige.jadb.managers.PackageManager @@ -72,17 +72,17 @@ internal sealed class AdbManager(deviceSerial: String? = null) : Closeable { apk.packageName ?: throw IllegalArgumentException("Package name is required") ) - device.copyFile(apk.file, PATH_INIT_PUSH) + device.push(apk.file, TMP_PATH) - device.run("$COMMAND_CREATE_DIR $PATH_INSTALLATION") - device.run(COMMAND_PREPARE_MOUNT_APK.applyReplacement()) + device.run("$CREATE_DIR $INSTALLATION_PATH") + device.run(INSTALL_PATCHED_APK.applyReplacement()) - device.createFile(PATH_INIT_PUSH, CONTENT_MOUNT_SCRIPT.applyReplacement()) + device.createFile(TMP_PATH, MOUNT_SCRIPT.applyReplacement()) - device.run(COMMAND_INSTALL_MOUNT.applyReplacement()) - device.run(COMMAND_UMOUNT.applyReplacement()) // Sanity check. - device.run(PATH_MOUNT.applyReplacement()) - device.run(COMMAND_RESTART.applyReplacement()) + device.run(INSTALL_MOUNT.applyReplacement()) + device.run(UMOUNT.applyReplacement()) // Sanity check. + device.run(MOUNT_PATH.applyReplacement()) + device.run(RESTART.applyReplacement()) super.install(apk) } @@ -92,9 +92,9 @@ internal sealed class AdbManager(deviceSerial: String? = null) : Closeable { val applyReplacement = getPlaceholderReplacement(packageName) - device.run(COMMAND_UMOUNT.applyReplacement(packageName)) - device.run(COMMAND_DELETE.applyReplacement(PATH_PATCHED_APK).applyReplacement()) - device.run(COMMAND_DELETE.applyReplacement(PATH_MOUNT).applyReplacement()) + device.run(UMOUNT.applyReplacement(packageName)) + device.run(DELETE.applyReplacement(PATCHED_APK_PATH).applyReplacement()) + device.run(DELETE.applyReplacement(MOUNT_PATH).applyReplacement()) super.uninstall(packageName) } diff --git a/src/main/kotlin/app/revanced/utils/adb/Commands.kt b/src/main/kotlin/app/revanced/utils/adb/Commands.kt index 744ebda..6d7f95d 100644 --- a/src/main/kotlin/app/revanced/utils/adb/Commands.kt +++ b/src/main/kotlin/app/revanced/utils/adb/Commands.kt @@ -18,8 +18,8 @@ internal fun JadbDevice.run(command: String, su: Boolean = false) = with(this.st internal fun JadbDevice.hasSu() = this.startCommand("su -h", false).waitFor() == 0 -internal fun JadbDevice.copyFile(file: File, targetFile: String) = - push(file, RemoteFile(targetFile)) +internal fun JadbDevice.push(file: File, targetFilePath: String) = + push(file, RemoteFile(targetFilePath)) internal fun JadbDevice.createFile(targetFile: String, content: String) = push(content.byteInputStream(), System.currentTimeMillis(), 644, RemoteFile(targetFile)) diff --git a/src/main/kotlin/app/revanced/utils/adb/Constants.kt b/src/main/kotlin/app/revanced/utils/adb/Constants.kt index 0d97211..96cc409 100644 --- a/src/main/kotlin/app/revanced/utils/adb/Constants.kt +++ b/src/main/kotlin/app/revanced/utils/adb/Constants.kt @@ -1,37 +1,37 @@ package app.revanced.utils.adb internal object Constants { - internal const val PLACEHOLDER = "TEMPLATE_PACKAGE_NAME" + internal const val PLACEHOLDER = "PLACEHOLDER" - internal const val PATH_INIT_PUSH = "/data/local/tmp/revanced.delete" - internal const val PATH_INSTALLATION = "/data/adb/revanced/" - internal const val PATH_PATCHED_APK = "$PATH_INSTALLATION$PLACEHOLDER.apk" - internal const val PATH_MOUNT = "/data/adb/service.d/mount_revanced_$PLACEHOLDER.sh" + internal const val TMP_PATH = "/data/local/tmp/revanced.tmp" + internal const val INSTALLATION_PATH = "/data/adb/revanced/" + internal const val PATCHED_APK_PATH = "$INSTALLATION_PATH$PLACEHOLDER.apk" + internal const val MOUNT_PATH = "/data/adb/service.d/mount_revanced_$PLACEHOLDER.sh" - internal const val COMMAND_DELETE = "rm -rf $PLACEHOLDER" - internal const val COMMAND_CREATE_DIR = "mkdir -p" - internal const val COMMAND_RESTART = "pm resolve-activity --brief $PLACEHOLDER | tail -n 1 | " + + internal const val DELETE = "rm -rf $PLACEHOLDER" + internal const val CREATE_DIR = "mkdir -p" + internal const val RESTART = "pm resolve-activity --brief $PLACEHOLDER | tail -n 1 | " + "xargs am start -n && kill ${'$'}(pidof -s $PLACEHOLDER)" - internal const val COMMAND_PREPARE_MOUNT_APK = "base_path=\"$PATH_PATCHED_APK\" && " + - "mv $PATH_INIT_PUSH ${'$'}base_path && " + + internal const val INSTALL_PATCHED_APK = "base_path=\"$PATCHED_APK_PATH\" && " + + "mv $TMP_PATH ${'$'}base_path && " + "chmod 644 ${'$'}base_path && " + "chown system:system ${'$'}base_path && " + "chcon u:object_r:apk_data_file:s0 ${'$'}base_path" - internal const val COMMAND_UMOUNT = + internal const val UMOUNT = "grep $PLACEHOLDER /proc/mounts | while read -r line; do echo ${'$'}line | cut -d \" \" -f 2 | sed 's/apk.*/apk/' | xargs -r umount -l; done" - internal const val COMMAND_INSTALL_MOUNT = "mv $PATH_INIT_PUSH $PATH_MOUNT && chmod +x $PATH_MOUNT" + internal const val INSTALL_MOUNT = "mv $TMP_PATH $MOUNT_PATH && chmod +x $MOUNT_PATH" - internal const val CONTENT_MOUNT_SCRIPT = + internal const val MOUNT_SCRIPT = """ #!/system/bin/sh MAGISKTMP="${'$'}(magisk --path)" || MAGISKTMP=/sbin MIRROR="${'$'}MAGISKTMP/.magisk/mirror" while [ "${'$'}(getprop sys.boot_completed | tr -d '\r')" != "1" ]; do sleep 1; done - base_path="$PATH_PATCHED_APK" + base_path="$PATCHED_APK_PATH" stock_path=${'$'}( pm path $PLACEHOLDER | grep base | sed 's/package://g' ) chcon u:object_r:apk_data_file:s0 ${'$'}base_path From a3d8705e89732a0dd4f51de28c405b6af13c8633 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Thu, 24 Aug 2023 16:24:33 +0200 Subject: [PATCH 47/62] fix: delete temporary files after root installation --- src/main/kotlin/app/revanced/utils/adb/AdbManager.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/kotlin/app/revanced/utils/adb/AdbManager.kt b/src/main/kotlin/app/revanced/utils/adb/AdbManager.kt index 70e456e..6ddfe4a 100644 --- a/src/main/kotlin/app/revanced/utils/adb/AdbManager.kt +++ b/src/main/kotlin/app/revanced/utils/adb/AdbManager.kt @@ -83,6 +83,7 @@ internal sealed class AdbManager(deviceSerial: String? = null) : Closeable { device.run(UMOUNT.applyReplacement()) // Sanity check. device.run(MOUNT_PATH.applyReplacement()) device.run(RESTART.applyReplacement()) + device.run(DELETE.applyReplacement(TMP_PATH).applyReplacement()) super.install(apk) } From 0350b7f1a276d9dc795b22442ba4f202855ea090 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Thu, 24 Aug 2023 16:50:10 +0200 Subject: [PATCH 48/62] feat: add install command This introduces a separate utility subcommand. --- docs/1_usage.md | 18 ++- .../cli/command/ListPatchesCommand.kt | 26 ++-- .../app/revanced/cli/command/MainCommand.kt | 7 +- .../revanced/cli/command/OptionsCommand.kt | 29 ++-- .../app/revanced/cli/command/PatchCommand.kt | 139 +++++++----------- .../cli/command/utility/InstallCommand.kt | 42 ++++++ .../command/{ => utility}/UninstallCommand.kt | 23 +-- .../cli/command/utility/UtilityCommand.kt | 10 ++ .../app/revanced/utils/adb/AdbManager.kt | 2 +- 9 files changed, 155 insertions(+), 141 deletions(-) create mode 100644 src/main/kotlin/app/revanced/cli/command/utility/InstallCommand.kt rename src/main/kotlin/app/revanced/cli/command/{ => utility}/UninstallCommand.kt (63%) create mode 100644 src/main/kotlin/app/revanced/cli/command/utility/UtilityCommand.kt diff --git a/docs/1_usage.md b/docs/1_usage.md index 13d9170..3f30c71 100644 --- a/docs/1_usage.md +++ b/docs/1_usage.md @@ -86,12 +86,26 @@ Learn how to ReVanced CLI. > **Note**: Some patches may require integrations such as [ReVanced Integrations](https://github.com/revanced/revanced-integrations). - Supply them with the option `-m`. If any patches accepted by ReVanced Patcher require ReVanced Integrations, + Supply them with the option `--merge`. If any patches accepted by ReVanced Patcher require ReVanced Integrations, they will be merged into the APK file automatically. - ### 🗑️ Uninstall a patched APK file ```bash - java -jar revanced-cli.jar uninstall \ + java -jar revanced-cli.jar utility uninstall \ --package-name \ ``` + + > **Note**: You can unmount an APK file + with the option `--unmount`. + +- ### ️ ⚙️ Manually install an APK file + + ```bash + java -jar revanced-cli.jar utility install \ + -a input.apk \ + + ``` + + > **Note**: You can mount an APK file + by supplying the package name of the app to mount the supplied APK file to over the option `--mount`. diff --git a/src/main/kotlin/app/revanced/cli/command/ListPatchesCommand.kt b/src/main/kotlin/app/revanced/cli/command/ListPatchesCommand.kt index 4ea13d2..28419d9 100644 --- a/src/main/kotlin/app/revanced/cli/command/ListPatchesCommand.kt +++ b/src/main/kotlin/app/revanced/cli/command/ListPatchesCommand.kt @@ -19,38 +19,33 @@ internal object ListPatchesCommand : Runnable { private val logger = Logger.getLogger(ListPatchesCommand::class.java.name) @Parameters( - description = ["Paths to patch bundles"], - arity = "1..*" + description = ["Paths to patch bundles"], arity = "1..*" ) - lateinit var patchBundles: Array + private lateinit var patchBundles: Array @Option( - names = ["-d", "--with-descriptions"], - description = ["List their descriptions"], - showDefaultValue = ALWAYS + names = ["-d", "--with-descriptions"], description = ["List their descriptions"], showDefaultValue = ALWAYS ) - var withDescriptions: Boolean = true + private var withDescriptions: Boolean = true @Option( names = ["-p", "--with-packages"], description = ["List the packages the patches are compatible with"], showDefaultValue = ALWAYS ) - var withPackages: Boolean = false + private var withPackages: Boolean = false @Option( names = ["-v", "--with-versions"], - description = ["List the versions of the packages the patches are compatible with"], + description = ["List the versions of the apps the patches are compatible with"], showDefaultValue = ALWAYS ) - var withVersions: Boolean = false + private var withVersions: Boolean = false @Option( - names = ["-o", "--with-options"], - description = ["List the options of the patches"], - showDefaultValue = ALWAYS + names = ["-o", "--with-options"], description = ["List the options of the patches"], showDefaultValue = ALWAYS ) - var withOptions: Boolean = false + private var withOptions: Boolean = false override fun run() { fun Package.buildString() = buildString { @@ -58,8 +53,7 @@ internal object ListPatchesCommand : Runnable { appendLine("Package name: $name") appendLine("Compatible versions:") append(versions.joinToString("\n") { version -> version }.prependIndent("\t")) - } else - append("Package name: $name") + } else append("Package name: $name") } fun PatchOption<*>.buildString() = buildString { diff --git a/src/main/kotlin/app/revanced/cli/command/MainCommand.kt b/src/main/kotlin/app/revanced/cli/command/MainCommand.kt index 20c6a47..f54c096 100644 --- a/src/main/kotlin/app/revanced/cli/command/MainCommand.kt +++ b/src/main/kotlin/app/revanced/cli/command/MainCommand.kt @@ -1,5 +1,6 @@ package app.revanced.cli.command +import app.revanced.cli.command.utility.UtilityCommand import app.revanced.patcher.patch.PatchClass import picocli.CommandLine import picocli.CommandLine.Command @@ -42,7 +43,7 @@ fun main(args: Array) { internal typealias PatchList = List -object CLIVersionProvider : IVersionProvider { +private object CLIVersionProvider : IVersionProvider { override fun getVersion(): Array { Properties().apply { load(MainCommand::class.java.getResourceAsStream("/app/revanced/cli/version.properties")) @@ -60,8 +61,8 @@ object CLIVersionProvider : IVersionProvider { subcommands = [ ListPatchesCommand::class, PatchCommand::class, - UninstallCommand::class, OptionsCommand::class, + UtilityCommand::class, ] ) -internal object MainCommand \ No newline at end of file +private object MainCommand \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/cli/command/OptionsCommand.kt b/src/main/kotlin/app/revanced/cli/command/OptionsCommand.kt index ed623d6..91f38f2 100644 --- a/src/main/kotlin/app/revanced/cli/command/OptionsCommand.kt +++ b/src/main/kotlin/app/revanced/cli/command/OptionsCommand.kt @@ -16,38 +16,31 @@ internal object OptionsCommand : Runnable { private val logger = Logger.getLogger(OptionsCommand::class.java.name) @CommandLine.Parameters( - description = ["Paths to patch bundles"], - arity = "1..*" + description = ["Paths to patch bundles"], arity = "1..*" ) - lateinit var patchBundles: Array + private lateinit var patchBundles: Array @CommandLine.Option( - names = ["-p", "--path"], - description = ["Path to patch options JSON file"], - showDefaultValue = ALWAYS + names = ["-p", "--path"], description = ["Path to patch options JSON file"], showDefaultValue = ALWAYS ) - var path: File = File("options.json") + private var path: File = File("options.json") @CommandLine.Option( - names = ["-o", "--overwrite"], - description = ["Overwrite existing options file"], - showDefaultValue = ALWAYS + names = ["-o", "--overwrite"], description = ["Overwrite existing options file"], showDefaultValue = ALWAYS ) - var overwrite: Boolean = false + private var overwrite: Boolean = false @CommandLine.Option( names = ["-u", "--update"], description = ["Update existing options by adding missing and removing non-existent options"], showDefaultValue = ALWAYS ) - var update: Boolean = false + private var update: Boolean = false - override fun run() = if (!path.exists() || overwrite) - with(PatchBundleLoader.Jar(*patchBundles)) { - if (update) setOptions(path) + override fun run() = if (!path.exists() || overwrite) with(PatchBundleLoader.Jar(*patchBundles)) { + if (update) setOptions(path) - Options.serialize(this, prettyPrint = true) - .let(path::writeText) - } + Options.serialize(this, prettyPrint = true).let(path::writeText) + } else logger.severe("Options file already exists, use --override to override it") } \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt b/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt index 3b6ce68..54d8986 100644 --- a/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt +++ b/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt @@ -23,84 +23,69 @@ import java.util.logging.Logger @CommandLine.Command( - name = "patch", - description = ["Patch the supplied APK file with the supplied patches and integrations"] + name = "patch", description = ["Patch the supplied APK file with the supplied patches and integrations"] ) -internal object PatchCommand: Runnable { +internal object PatchCommand : Runnable { private val logger = Logger.getLogger(PatchCommand::class.java.name) @CommandLine.Parameters( - description = ["APK file to be patched"], - arity = "1..1" + description = ["APK file to be patched"], arity = "1..1" ) - lateinit var apk: File + private lateinit var apk: File @CommandLine.Option( - names = ["-b", "--patch-bundle"], - description = ["One or more bundles of patches"], - required = true + names = ["-b", "--patch-bundle"], description = ["One or more bundles of patches"], required = true ) - var patchBundles = emptyList() + private var patchBundles = emptyList() @CommandLine.Option( - names = ["-m", "--merge"], - description = ["One or more DEX files or containers to merge into the APK"] + names = ["-m", "--merge"], description = ["One or more DEX files or containers to merge into the APK"] ) - var integrations = listOf() + private var integrations = listOf() @CommandLine.Option( - names = ["-i", "--include"], - description = ["List of patches to include"] + names = ["-i", "--include"], description = ["List of patches to include"] ) - var includedPatches = arrayOf() + private var includedPatches = arrayOf() @CommandLine.Option( - names = ["-e", "--exclude"], - description = ["List of patches to exclude"] + names = ["-e", "--exclude"], description = ["List of patches to exclude"] ) - var excludedPatches = arrayOf() + private var excludedPatches = arrayOf() @CommandLine.Option( - names = ["--options"], - description = ["Path to patch options JSON file"], - showDefaultValue = ALWAYS + names = ["--options"], description = ["Path to patch options JSON file"], showDefaultValue = ALWAYS ) - var optionsFile: File = File("options.json") + private var optionsFile: File = File("options.json") @CommandLine.Option( names = ["--exclusive"], description = ["Only include patches that are explicitly specified to be included"], showDefaultValue = ALWAYS ) - var exclusive = false + private var exclusive = false @CommandLine.Option( names = ["--experimental"], description = ["Ignore patches incompatibility to versions"], showDefaultValue = ALWAYS ) - var experimental: Boolean = false + private var experimental: Boolean = false @CommandLine.Option( - names = ["-o", "--out"], - description = ["Path to save the patched APK file to"], - required = true + names = ["-o", "--out"], description = ["Path to save the patched APK file to"], required = true ) - lateinit var outputFilePath: File + private lateinit var outputFilePath: File @CommandLine.Option( - names = ["-d", "--device-serial"], - description = ["ADB device serial to install to"], - showDefaultValue = ALWAYS + names = ["-d", "--device-serial"], description = ["ADB device serial to install to"], showDefaultValue = ALWAYS ) - var deviceSerial: String? = null + private var deviceSerial: String? = null @CommandLine.Option( - names = ["--mount"], - description = ["Install by mounting the patched package"], - showDefaultValue = ALWAYS + names = ["--mount"], description = ["Install by mounting the patched APK file"], showDefaultValue = ALWAYS ) - var mount: Boolean = false + private var mount: Boolean = false @CommandLine.Option( names = ["--common-name"], @@ -108,39 +93,36 @@ internal object PatchCommand: Runnable { showDefaultValue = ALWAYS ) - var commonName = "ReVanced" + private var commonName = "ReVanced" @CommandLine.Option( - names = ["--keystore"], - description = ["Path to the keystore to sign the patched APK file with"] + names = ["--keystore"], description = ["Path to the keystore to sign the patched APK file with"] ) - var keystorePath: String? = null + private var keystorePath: String? = null @CommandLine.Option( - names = ["--password"], - description = ["The password of the keystore to sign the patched APK file with"] + names = ["--password"], description = ["The password of the keystore to sign the patched APK file with"] ) - var password = "ReVanced" + private var password = "ReVanced" @CommandLine.Option( names = ["-r", "--resource-cache"], description = ["Path to temporary resource cache directory"], showDefaultValue = ALWAYS ) - var resourceCachePath = File("revanced-resource-cache") + private var resourceCachePath = File("revanced-resource-cache") @CommandLine.Option( - names = ["--custom-aapt2-binary"], - description = ["Path to a custom AAPT binary to compile resources with"] + names = ["--custom-aapt2-binary"], description = ["Path to a custom AAPT binary to compile resources with"] ) - var aaptBinaryPath = File("") + private var aaptBinaryPath = File("") @CommandLine.Option( names = ["-p", "--purge"], description = ["Purge the temporary resource cache directory after patching"], showDefaultValue = ALWAYS ) - var purge: Boolean = false + private var purge: Boolean = false override fun run() { // region Prepare @@ -206,8 +188,7 @@ internal object PatchCommand: Runnable { val alignAndSignedFile = sign( apk.newAlignedFile( - result, - resourceCachePath.resolve("${outputFilePath.nameWithoutExtension}_aligned.apk") + result, resourceCachePath.resolve("${outputFilePath.nameWithoutExtension}_aligned.apk") ) ) @@ -287,19 +268,16 @@ internal object PatchCommand: Runnable { it.isEmpty() || it.any { version -> version == packageVersion } } - if (!matchesVersion) return@patch logger.warning( - "${patch.patchName} is incompatible with version $packageVersion. " + - "This patch is only compatible with version " + - packages.joinToString(";") { pkg -> - "${pkg.name}: ${pkg.versions.joinToString(", ")}" - } - ) + if (!matchesVersion) return@patch logger.warning("${patch.patchName} is incompatible with version $packageVersion. " + "This patch is only compatible with version " + packages.joinToString( + ";" + ) { pkg -> + "${pkg.name}: ${pkg.versions.joinToString(", ")}" + }) - } ?: return@patch logger.fine( - "${patch.patchName} is incompatible with $packageName. " + - "This patch is only compatible with " + - packages.joinToString(", ") { `package` -> `package`.name } - ) + } + ?: return@patch logger.fine("${patch.patchName} is incompatible with $packageName. " + "This patch is only compatible with " + packages.joinToString( + ", " + ) { `package` -> `package`.name }) return@let } ?: logger.fine("$formattedPatchName: No constraint on packages.") @@ -341,8 +319,7 @@ internal object PatchCommand: Runnable { * @param outputFile The file to save the aligned APK to. */ private fun File.newAlignedFile( - result: PatcherResult, - outputFile: File + result: PatcherResult, outputFile: File ): File { logger.info("Aligning $name") @@ -351,23 +328,20 @@ internal object PatchCommand: Runnable { ZipFile(outputFile).use { file -> result.dexFiles.forEach { file.addEntryCompressData( - ZipEntry.createWithName(it.name), - it.stream.readBytes() + ZipEntry.createWithName(it.name), it.stream.readBytes() ) } result.resourceFile?.let { file.copyEntriesFromFileAligned( - ZipFile(it), - ZipAligner::getEntryAlignment + ZipFile(it), ZipAligner::getEntryAlignment ) } // TODO: Do not compress result.doNotCompress file.copyEntriesFromFileAligned( - ZipFile(this), - ZipAligner::getEntryAlignment + ZipFile(this), ZipAligner::getEntryAlignment ) } @@ -380,32 +354,25 @@ internal object PatchCommand: Runnable { * @param inputFile The APK file to sign. * @return The signed APK file. If [mount] is true, the input file will be returned. */ - private fun sign(inputFile: File) = if (mount) - inputFile + private fun sign(inputFile: File) = if (mount) inputFile else { logger.info("Signing ${inputFile.name}") - val keyStoreFilePath = keystorePath ?: outputFilePath - .absoluteFile.parentFile.resolve("${outputFilePath.nameWithoutExtension}.keystore").canonicalPath + val keyStoreFilePath = keystorePath + ?: outputFilePath.absoluteFile.parentFile.resolve("${outputFilePath.nameWithoutExtension}.keystore").canonicalPath val options = SigningOptions( - commonName, - password, - keyStoreFilePath + commonName, password, keyStoreFilePath ) - ApkSigner(options) - .signApk( - inputFile, - resourceCachePath.resolve("${outputFilePath.nameWithoutExtension}_signed.apk") + ApkSigner(options).signApk( + inputFile, resourceCachePath.resolve("${outputFilePath.nameWithoutExtension}_signed.apk") ) } private fun purge(resourceCachePath: File) { - val result = if (resourceCachePath.deleteRecursively()) - "Purged resource cache directory" - else - "Failed to purge resource cache directory" + val result = if (resourceCachePath.deleteRecursively()) "Purged resource cache directory" + else "Failed to purge resource cache directory" logger.info(result) } } \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/cli/command/utility/InstallCommand.kt b/src/main/kotlin/app/revanced/cli/command/utility/InstallCommand.kt new file mode 100644 index 0000000..593680d --- /dev/null +++ b/src/main/kotlin/app/revanced/cli/command/utility/InstallCommand.kt @@ -0,0 +1,42 @@ +package app.revanced.cli.command.utility + +import app.revanced.utils.adb.AdbManager +import picocli.CommandLine.* +import java.io.File +import java.util.logging.Logger + + +@Command( + name = "install", description = ["Install an APK file to devices with the supplied ADB device serials"] +) +internal object InstallCommand : Runnable { + private val logger = Logger.getLogger(InstallCommand::class.java.name) + + @Parameters( + description = ["ADB device serials"], arity = "1..*" + ) + private lateinit var deviceSerials: Array + + @Option( + names = ["-a", "--apk"], description = ["APK file to be installed"], required = true + ) + private lateinit var apk: File + + @Option( + names = ["-m", "--mount"], + description = ["Mount the supplied APK file over the app with the supplied package name"], + ) + private var packageName: String? = null + + override fun run() = try { + deviceSerials.forEach { deviceSerial -> + if (packageName != null) { + AdbManager.RootAdbManager(deviceSerial) + } else { + AdbManager.UserAdbManager(deviceSerial) + }.install(AdbManager.Apk(apk, packageName)) + } + } catch (e: AdbManager.DeviceNotFoundException) { + logger.severe(e.toString()) + } +} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/cli/command/UninstallCommand.kt b/src/main/kotlin/app/revanced/cli/command/utility/UninstallCommand.kt similarity index 63% rename from src/main/kotlin/app/revanced/cli/command/UninstallCommand.kt rename to src/main/kotlin/app/revanced/cli/command/utility/UninstallCommand.kt index a0f7f8d..d8dc336 100644 --- a/src/main/kotlin/app/revanced/cli/command/UninstallCommand.kt +++ b/src/main/kotlin/app/revanced/cli/command/utility/UninstallCommand.kt @@ -1,4 +1,4 @@ -package app.revanced.cli.command +package app.revanced.cli.command.utility import app.revanced.utils.adb.AdbManager import picocli.CommandLine.* @@ -13,28 +13,21 @@ import java.util.logging.Logger internal object UninstallCommand : Runnable { private val logger = Logger.getLogger(UninstallCommand::class.java.name) - @Parameters( - description = ["ADB device serials"], - arity = "1..*" - ) - lateinit var deviceSerials: Array + @Parameters(description = ["ADB device serials"], arity = "1..*") + private lateinit var deviceSerials: Array - @Option( - names = ["-p", "--package-name"], - description = ["Package name to uninstall"], - required = true - ) - lateinit var packageName: String + @Option(names = ["-p", "--package-name"], description = ["Package name to uninstall"], required = true) + private lateinit var packageName: String @Option( names = ["-u", "--unmount"], - description = ["Uninstall by unmounting the patched package"], + description = ["Uninstall by unmounting the patched APK file"], showDefaultValue = ALWAYS ) - var unmount: Boolean = false + private var unmount: Boolean = false override fun run() = try { - deviceSerials.forEach {deviceSerial -> + deviceSerials.forEach { deviceSerial -> if (unmount) { AdbManager.RootAdbManager(deviceSerial) } else { diff --git a/src/main/kotlin/app/revanced/cli/command/utility/UtilityCommand.kt b/src/main/kotlin/app/revanced/cli/command/utility/UtilityCommand.kt new file mode 100644 index 0000000..70c77c6 --- /dev/null +++ b/src/main/kotlin/app/revanced/cli/command/utility/UtilityCommand.kt @@ -0,0 +1,10 @@ +package app.revanced.cli.command.utility + +import picocli.CommandLine + +@CommandLine.Command( + name = "utility", + description = ["Commands for utility purposes"], + subcommands = [InstallCommand::class, UninstallCommand::class], +) +internal object UtilityCommand \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/utils/adb/AdbManager.kt b/src/main/kotlin/app/revanced/utils/adb/AdbManager.kt index 6ddfe4a..ef27e11 100644 --- a/src/main/kotlin/app/revanced/utils/adb/AdbManager.kt +++ b/src/main/kotlin/app/revanced/utils/adb/AdbManager.kt @@ -89,7 +89,7 @@ internal sealed class AdbManager(deviceSerial: String? = null) : Closeable { } override fun uninstall(packageName: String) { - logger.info("Uninstalling $packageName by unmounting and deleting the package") + logger.info("Uninstalling $packageName by unmounting") val applyReplacement = getPlaceholderReplacement(packageName) From 3dd875d14cca488ade6d21bbd4cce0d481692134 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Thu, 24 Aug 2023 16:53:31 +0200 Subject: [PATCH 49/62] feat: use friendly descriptions --- .../app/revanced/cli/command/utility/UninstallCommand.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/app/revanced/cli/command/utility/UninstallCommand.kt b/src/main/kotlin/app/revanced/cli/command/utility/UninstallCommand.kt index d8dc336..e17bec7 100644 --- a/src/main/kotlin/app/revanced/cli/command/utility/UninstallCommand.kt +++ b/src/main/kotlin/app/revanced/cli/command/utility/UninstallCommand.kt @@ -8,7 +8,7 @@ import java.util.logging.Logger @Command( name = "uninstall", - description = ["Uninstall a patched APK file from the devices with the supplied ADB device serials"] + description = ["Uninstall a patched app from the devices with the supplied ADB device serials"] ) internal object UninstallCommand : Runnable { private val logger = Logger.getLogger(UninstallCommand::class.java.name) @@ -16,7 +16,7 @@ internal object UninstallCommand : Runnable { @Parameters(description = ["ADB device serials"], arity = "1..*") private lateinit var deviceSerials: Array - @Option(names = ["-p", "--package-name"], description = ["Package name to uninstall"], required = true) + @Option(names = ["-p", "--package-name"], description = ["Package name of the app to uninstall"], required = true) private lateinit var packageName: String @Option( From 52c3be23f2915dccaee7f9941413c8f81e14acc8 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Thu, 24 Aug 2023 16:53:54 +0200 Subject: [PATCH 50/62] fix: also delete temporary files when uninstalling --- src/main/kotlin/app/revanced/utils/adb/AdbManager.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/kotlin/app/revanced/utils/adb/AdbManager.kt b/src/main/kotlin/app/revanced/utils/adb/AdbManager.kt index ef27e11..1bd0043 100644 --- a/src/main/kotlin/app/revanced/utils/adb/AdbManager.kt +++ b/src/main/kotlin/app/revanced/utils/adb/AdbManager.kt @@ -96,6 +96,7 @@ internal sealed class AdbManager(deviceSerial: String? = null) : Closeable { device.run(UMOUNT.applyReplacement(packageName)) device.run(DELETE.applyReplacement(PATCHED_APK_PATH).applyReplacement()) device.run(DELETE.applyReplacement(MOUNT_PATH).applyReplacement()) + device.run(DELETE.applyReplacement(TMP_PATH).applyReplacement()) super.uninstall(packageName) } From 2c7fcaf4add65a12052afc5bef779dbc73debd69 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Thu, 24 Aug 2023 17:51:31 +0200 Subject: [PATCH 51/62] fix: fix running commands not running --- .../app/revanced/cli/command/MainCommand.kt | 11 +++++----- .../kotlin/app/revanced/utils/adb/Commands.kt | 22 +++++++++++-------- .../app/revanced/utils/adb/Constants.kt | 4 ++-- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/main/kotlin/app/revanced/cli/command/MainCommand.kt b/src/main/kotlin/app/revanced/cli/command/MainCommand.kt index f54c096..99ac1b1 100644 --- a/src/main/kotlin/app/revanced/cli/command/MainCommand.kt +++ b/src/main/kotlin/app/revanced/cli/command/MainCommand.kt @@ -18,12 +18,11 @@ fun main(args: Array) { } object : Handler() { - override fun publish(record: LogRecord) = formatter.format(record).let { - if (record.level.intValue() > Level.INFO.intValue()) { - System.err.write(it.toByteArray()) - } else { - System.out.write(it.toByteArray()) - } + override fun publish(record: LogRecord) = formatter.format(record).toByteArray().let { + if (record.level.intValue() > Level.INFO.intValue()) + System.err.write(it) + else + System.out.write(it) } override fun flush() { diff --git a/src/main/kotlin/app/revanced/utils/adb/Commands.kt b/src/main/kotlin/app/revanced/utils/adb/Commands.kt index 6d7f95d..7ec7eb8 100644 --- a/src/main/kotlin/app/revanced/utils/adb/Commands.kt +++ b/src/main/kotlin/app/revanced/utils/adb/Commands.kt @@ -2,17 +2,21 @@ package app.revanced.utils.adb import se.vidstige.jadb.JadbDevice import se.vidstige.jadb.RemoteFile +import se.vidstige.jadb.ShellProcessBuilder import java.io.File -import java.util.concurrent.Callable -import java.util.concurrent.Executors -// return the input or output stream, depending on which first returns a value -internal fun JadbDevice.run(command: String, su: Boolean = false) = with(this.startCommand(command, su)) { - Executors.newFixedThreadPool(2).let { service -> - arrayOf(inputStream, errorStream).map { stream -> - Callable { stream.bufferedReader().use { it.readLine() } } - }.let { tasks -> service.invokeAny(tasks).also { service.shutdown() } } - } + +internal fun JadbDevice.buildCommand(command: String, su: Boolean = true): ShellProcessBuilder { + if (su) return shellProcessBuilder("su -c \'$command\'") + + val args = command.split(" ") as ArrayList + val cmd = args.removeFirst() + + return shellProcessBuilder(cmd, *args.toTypedArray()) +} + +internal fun JadbDevice.run(command: String, su: Boolean = true): Int { + return this.buildCommand(command, su).start().waitFor() } internal fun JadbDevice.hasSu() = diff --git a/src/main/kotlin/app/revanced/utils/adb/Constants.kt b/src/main/kotlin/app/revanced/utils/adb/Constants.kt index 96cc409..ea5095e 100644 --- a/src/main/kotlin/app/revanced/utils/adb/Constants.kt +++ b/src/main/kotlin/app/revanced/utils/adb/Constants.kt @@ -24,7 +24,7 @@ internal object Constants { internal const val INSTALL_MOUNT = "mv $TMP_PATH $MOUNT_PATH && chmod +x $MOUNT_PATH" - internal const val MOUNT_SCRIPT = + internal val MOUNT_SCRIPT = """ #!/system/bin/sh MAGISKTMP="${'$'}(magisk --path)" || MAGISKTMP=/sbin @@ -36,5 +36,5 @@ internal object Constants { chcon u:object_r:apk_data_file:s0 ${'$'}base_path mount -o bind ${'$'}MIRROR${'$'}base_path ${'$'}stock_path - """ + """.trimIndent() } From 1d26e572f7c1a1edb6bcca029e8e174053607058 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 24 Aug 2023 15:53:09 +0000 Subject: [PATCH 52/62] chore(release): 3.0.0-dev.5 [skip ci] # [3.0.0-dev.5](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.4...v3.0.0-dev.5) (2023-08-24) ### Bug Fixes * also delete temporary files when uninstalling ([52c3be2](https://github.com/ReVanced/revanced-cli/commit/52c3be23f2915dccaee7f9941413c8f81e14acc8)) * delete temporary files after root installation ([a3d8705](https://github.com/ReVanced/revanced-cli/commit/a3d8705e89732a0dd4f51de28c405b6af13c8633)) * fix running commands not running ([2c7fcaf](https://github.com/ReVanced/revanced-cli/commit/2c7fcaf4add65a12052afc5bef779dbc73debd69)) * only check once for patch options ([11c3a6c](https://github.com/ReVanced/revanced-cli/commit/11c3a6cfd4fe59ba5d703358634a1853e1cc22a5)) ### Features * add install command ([0350b7f](https://github.com/ReVanced/revanced-cli/commit/0350b7f1a276d9dc795b22442ba4f202855ea090)) * use friendly descriptions ([3dd875d](https://github.com/ReVanced/revanced-cli/commit/3dd875d14cca488ade6d21bbd4cce0d481692134)) --- CHANGELOG.md | 16 ++++++++++++++++ gradle.properties | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3125265..c23fb1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ +# [3.0.0-dev.5](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.4...v3.0.0-dev.5) (2023-08-24) + + +### Bug Fixes + +* also delete temporary files when uninstalling ([52c3be2](https://github.com/ReVanced/revanced-cli/commit/52c3be23f2915dccaee7f9941413c8f81e14acc8)) +* delete temporary files after root installation ([a3d8705](https://github.com/ReVanced/revanced-cli/commit/a3d8705e89732a0dd4f51de28c405b6af13c8633)) +* fix running commands not running ([2c7fcaf](https://github.com/ReVanced/revanced-cli/commit/2c7fcaf4add65a12052afc5bef779dbc73debd69)) +* only check once for patch options ([11c3a6c](https://github.com/ReVanced/revanced-cli/commit/11c3a6cfd4fe59ba5d703358634a1853e1cc22a5)) + + +### Features + +* add install command ([0350b7f](https://github.com/ReVanced/revanced-cli/commit/0350b7f1a276d9dc795b22442ba4f202855ea090)) +* use friendly descriptions ([3dd875d](https://github.com/ReVanced/revanced-cli/commit/3dd875d14cca488ade6d21bbd4cce0d481692134)) + # [3.0.0-dev.4](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.3...v3.0.0-dev.4) (2023-08-24) diff --git a/gradle.properties b/gradle.properties index 5121968..d60c4e6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true kotlin.code.style = official -version = 3.0.0-dev.4 +version = 3.0.0-dev.5 From 139e7facac3f0d6d3d403d6efbadea8fc54a536e Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Thu, 24 Aug 2023 18:09:25 +0200 Subject: [PATCH 53/62] build(Needs bump): depend on build task when publishing This fixes the issue that no builds are generated --- build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle.kts b/build.gradle.kts index 0542237..9c6b335 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -52,5 +52,6 @@ tasks { register("publish") { group = "publish" description = "Dummy task" + dependsOn(build) } } From 8dd709b6ef27874a63f30c99ac7905c870f8f4e6 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 24 Aug 2023 16:11:51 +0000 Subject: [PATCH 54/62] chore(release): 3.0.0-dev.6 [skip ci] # [3.0.0-dev.6](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.5...v3.0.0-dev.6) (2023-08-24) --- CHANGELOG.md | 2 ++ gradle.properties | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c23fb1f..f73d6b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +# [3.0.0-dev.6](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.5...v3.0.0-dev.6) (2023-08-24) + # [3.0.0-dev.5](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.4...v3.0.0-dev.5) (2023-08-24) diff --git a/gradle.properties b/gradle.properties index d60c4e6..70df0c0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true kotlin.code.style = official -version = 3.0.0-dev.5 +version = 3.0.0-dev.6 From 924c1f80ec0d17a3bdc07a0fb2015e44c49162e4 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Thu, 24 Aug 2023 23:45:10 +0200 Subject: [PATCH 55/62] fix: print stack trace when a patch failed --- src/main/kotlin/app/revanced/cli/command/PatchCommand.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt b/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt index 54d8986..1df5d81 100644 --- a/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt +++ b/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt @@ -19,6 +19,8 @@ import kotlinx.coroutines.runBlocking import picocli.CommandLine import picocli.CommandLine.Help.Visibility.ALWAYS import java.io.File +import java.io.PrintWriter +import java.io.StringWriter import java.util.logging.Logger @@ -174,7 +176,10 @@ internal object PatchCommand : Runnable { runBlocking { apply(false).collect { patchResult -> patchResult.exception?.let { - logger.severe("${patchResult.patchName} failed:\n${patchResult.exception}") + StringWriter().use { writer -> + it.printStackTrace(PrintWriter(writer)) + logger.severe("${patchResult.patchName} failed: $writer") + } } ?: logger.info("${patchResult.patchName} succeeded") } } From 6aed9461837a85f9f808510184b338d7591809aa Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 24 Aug 2023 21:47:17 +0000 Subject: [PATCH 56/62] chore(release): 3.0.0-dev.7 [skip ci] # [3.0.0-dev.7](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.6...v3.0.0-dev.7) (2023-08-24) ### Bug Fixes * print stack trace when a patch failed ([924c1f8](https://github.com/ReVanced/revanced-cli/commit/924c1f80ec0d17a3bdc07a0fb2015e44c49162e4)) --- CHANGELOG.md | 7 +++++++ gradle.properties | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f73d6b6..b1fd3d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [3.0.0-dev.7](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.6...v3.0.0-dev.7) (2023-08-24) + + +### Bug Fixes + +* print stack trace when a patch failed ([924c1f8](https://github.com/ReVanced/revanced-cli/commit/924c1f80ec0d17a3bdc07a0fb2015e44c49162e4)) + # [3.0.0-dev.6](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.5...v3.0.0-dev.6) (2023-08-24) # [3.0.0-dev.5](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.4...v3.0.0-dev.5) (2023-08-24) diff --git a/gradle.properties b/gradle.properties index 70df0c0..115ab45 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true kotlin.code.style = official -version = 3.0.0-dev.6 +version = 3.0.0-dev.7 From 0f3e090418771e951dfd15e5c193421f72cbe459 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Fri, 25 Aug 2023 01:29:29 +0200 Subject: [PATCH 57/62] fix: do not delete output file This fixes the output file to be deleted when the option `--purge` was used. --- src/main/kotlin/app/revanced/cli/command/PatchCommand.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt b/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt index 1df5d81..2c049c4 100644 --- a/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt +++ b/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt @@ -204,7 +204,6 @@ internal object PatchCommand : Runnable { if (purge) { logger.info("Purging temporary files") - outputFilePath.delete() purge(resourceCachePath) } From 3a198052bb90cde896ff8690a9ae2c7654f7675d Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 24 Aug 2023 23:32:29 +0000 Subject: [PATCH 58/62] chore(release): 3.0.0-dev.8 [skip ci] # [3.0.0-dev.8](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.7...v3.0.0-dev.8) (2023-08-24) ### Bug Fixes * do not delete output file ([0f3e090](https://github.com/ReVanced/revanced-cli/commit/0f3e090418771e951dfd15e5c193421f72cbe459)) --- CHANGELOG.md | 7 +++++++ gradle.properties | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b1fd3d7..bf6c687 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [3.0.0-dev.8](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.7...v3.0.0-dev.8) (2023-08-24) + + +### Bug Fixes + +* do not delete output file ([0f3e090](https://github.com/ReVanced/revanced-cli/commit/0f3e090418771e951dfd15e5c193421f72cbe459)) + # [3.0.0-dev.7](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.6...v3.0.0-dev.7) (2023-08-24) diff --git a/gradle.properties b/gradle.properties index 115ab45..c089d51 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true kotlin.code.style = official -version = 3.0.0-dev.7 +version = 3.0.0-dev.8 From c93186fb9700907e65f33442e88073783cc163de Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Fri, 25 Aug 2023 02:26:27 +0200 Subject: [PATCH 59/62] feat: Check for missing integrations Check, if the integrations file exists at first. --- .../kotlin/app/revanced/cli/command/PatchCommand.kt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt b/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt index 2c049c4..87bfcda 100644 --- a/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt +++ b/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt @@ -130,7 +130,16 @@ internal object PatchCommand : Runnable { // region Prepare if (!apk.exists()) { - logger.severe("Input file ${apk.name} does not exist") + logger.severe("APK file ${apk.name} does not exist") + return + } + + integrations.filter { !it.exists() }.let { + if (it.isEmpty()) return@let + + it.forEach { integration -> + logger.severe("Integration file ${integration.name} does not exist") + } return } From be5d812dff178247122bd4c859a5a28d39018632 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Fri, 25 Aug 2023 00:28:57 +0000 Subject: [PATCH 60/62] chore(release): 3.0.0-dev.9 [skip ci] # [3.0.0-dev.9](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.8...v3.0.0-dev.9) (2023-08-25) ### Features * Check for missing integrations ([c93186f](https://github.com/ReVanced/revanced-cli/commit/c93186fb9700907e65f33442e88073783cc163de)) --- CHANGELOG.md | 7 +++++++ gradle.properties | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf6c687..7cee5b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [3.0.0-dev.9](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.8...v3.0.0-dev.9) (2023-08-25) + + +### Features + +* Check for missing integrations ([c93186f](https://github.com/ReVanced/revanced-cli/commit/c93186fb9700907e65f33442e88073783cc163de)) + # [3.0.0-dev.8](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.7...v3.0.0-dev.8) (2023-08-24) diff --git a/gradle.properties b/gradle.properties index c089d51..7dbefd9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true kotlin.code.style = official -version = 3.0.0-dev.8 +version = 3.0.0-dev.9 From 2d5a7fdf1eb2e13f5013a790b03f09851b167fe0 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Fri, 25 Aug 2023 23:47:05 +0200 Subject: [PATCH 61/62] fix: filtration of patches malfunctioning Apparently, you were not able to include patches explicitly --- .../app/revanced/cli/command/PatchCommand.kt | 85 ++++--------------- 1 file changed, 18 insertions(+), 67 deletions(-) diff --git a/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt b/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt index 87bfcda..4bc2641 100644 --- a/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt +++ b/src/main/kotlin/app/revanced/cli/command/PatchCommand.kt @@ -68,11 +68,11 @@ internal object PatchCommand : Runnable { private var exclusive = false @CommandLine.Option( - names = ["--experimental"], - description = ["Ignore patches incompatibility to versions"], + names = ["-f","--force"], + description = ["Force inclusion of patches that are incompatible with the supplied APK file's version"], showDefaultValue = ALWAYS ) - private var experimental: Boolean = false + private var force: Boolean = false @CommandLine.Option( names = ["-o", "--out"], description = ["Path to save the patched APK file to"], required = true @@ -225,8 +225,8 @@ internal object PatchCommand : Runnable { * - [includedPatches] (explicitly included) * - [excludedPatches] (explicitly excluded) * - [exclusive] (only include patches that are explicitly included) - * - [experimental] (ignore patches incompatibility to versions) - * - package name and version of the input APK file (if [experimental] is false) + * - [force] (ignore patches incompatibility to versions) + * - Package name and version of the input APK file (if [force] is false) * * @param patches The patches to filter. * @return The filtered patches. @@ -238,46 +238,22 @@ internal object PatchCommand : Runnable { patches.forEach patch@{ patch -> val formattedPatchName = patch.patchName.lowercase().replace(" ", "-") - /** - * Check if the patch is explicitly excluded. - * - * Cases: - * 1. -e patch.name - * 2. -i patch.name -e patch.name - */ + val explicitlyExcluded = excludedPatches.contains(formattedPatchName) + if (explicitlyExcluded) return@patch logger.info("Excluding ${patch.patchName}") - /** - * Check if the patch is explicitly excluded. - * - * Cases: - * 1. -e patch.name - * 2. -i patch.name -e patch.name - */ + // If the patch is explicitly included, it will be included if [exclusive] is false. + val explicitlyIncluded = exclusive && includedPatches.contains(formattedPatchName) - val excluded = excludedPatches.contains(formattedPatchName) - if (excluded) return@patch logger.info("Excluding ${patch.patchName}") + // If the patch is implicitly included, it will be only included if [exclusive] is false. + val implicitlyIncluded = !exclusive && patch.include - /** - * Check if the patch is constrained to packages. - */ - - /** - * Check if the patch is constrained to packages. - */ + val included = implicitlyIncluded || explicitlyIncluded + if (!included) return@patch logger.info("${patch.patchName} excluded by default") // Case 1. + // At last make sure the patch is compatible with the supplied APK files package name and version. patch.compatiblePackages?.let { packages -> packages.singleOrNull { it.name == packageName }?.let { `package` -> - /** - * Check if the package version matches. - * If experimental is true, version matching will be skipped. - */ - - /** - * Check if the package version matches. - * If experimental is true, version matching will be skipped. - */ - - val matchesVersion = experimental || `package`.versions.let { + val matchesVersion = force || `package`.versions.let { it.isEmpty() || it.any { version -> version == packageVersion } } @@ -287,38 +263,13 @@ internal object PatchCommand : Runnable { "${pkg.name}: ${pkg.versions.joinToString(", ")}" }) - } - ?: return@patch logger.fine("${patch.patchName} is incompatible with $packageName. " + "This patch is only compatible with " + packages.joinToString( - ", " - ) { `package` -> `package`.name }) + } ?: return@patch logger.fine("${patch.patchName} is incompatible with $packageName. " + + "This patch is only compatible with " + + packages.joinToString(", ") { `package` -> `package`.name }) return@let } ?: logger.fine("$formattedPatchName: No constraint on packages.") - /** - * Check if the patch is explicitly included. - * - * Cases: - * 1. --exclusive - * 2. --exclusive -i patch.name - */ - - /** - * Check if the patch is explicitly included. - * - * Cases: - * 1. --exclusive - * 2. --exclusive -i patch.name - */ - - val explicitlyIncluded = includedPatches.contains(formattedPatchName) - - val implicitlyIncluded = !exclusive && patch.include // Case 3. - val exclusivelyIncluded = exclusive && explicitlyIncluded // Case 2. - - val included = implicitlyIncluded || exclusivelyIncluded - if (!included) return@patch logger.info("${patch.patchName} excluded by default") // Case 1. - logger.fine("Adding $formattedPatchName") add(patch) From 9e39a6f8e4a47af6a0d94eae6c559ad8c46874b3 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Fri, 25 Aug 2023 21:48:59 +0000 Subject: [PATCH 62/62] chore(release): 3.0.0-dev.10 [skip ci] # [3.0.0-dev.10](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.9...v3.0.0-dev.10) (2023-08-25) ### Bug Fixes * filtration of patches malfunctioning ([2d5a7fd](https://github.com/ReVanced/revanced-cli/commit/2d5a7fdf1eb2e13f5013a790b03f09851b167fe0)) --- CHANGELOG.md | 7 +++++++ gradle.properties | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cee5b8..c0d657d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [3.0.0-dev.10](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.9...v3.0.0-dev.10) (2023-08-25) + + +### Bug Fixes + +* filtration of patches malfunctioning ([2d5a7fd](https://github.com/ReVanced/revanced-cli/commit/2d5a7fdf1eb2e13f5013a790b03f09851b167fe0)) + # [3.0.0-dev.9](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.8...v3.0.0-dev.9) (2023-08-25) diff --git a/gradle.properties b/gradle.properties index 7dbefd9..7fe5f91 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true kotlin.code.style = official -version = 3.0.0-dev.9 +version = 3.0.0-dev.10