Compare commits

...

108 Commits

Author SHA1 Message Date
semantic-release-bot
5d50d1a622 chore(release): 3.1.0 [skip ci]
# [3.1.0](https://github.com/ReVanced/revanced-cli/compare/v3.0.1...v3.1.0) (2023-08-31)

### Bug Fixes

* check for package compatibility at first ([9fe5a0b](9fe5a0b6d9))
* do not filter explicitly included patches ([a3d8f00](a3d8f004ec))
* format patches input ([bbb1a63](bbb1a63abd))

### Features

* Simplify command description ([3b3f7c7](3b3f7c7a7a))
2023-08-31 20:32:08 +00:00
oSumAtrIX
83c28d9f71 chore: merge branch dev to main (#255) 2023-08-31 22:30:35 +02:00
semantic-release-bot
93cbcc28aa chore(release): 3.1.0-dev.1 [skip ci]
# [3.1.0-dev.1](https://github.com/ReVanced/revanced-cli/compare/v3.0.2-dev.2...v3.1.0-dev.1) (2023-08-28)

### Bug Fixes

* format patches input ([bbb1a63](bbb1a63abd))

### Features

* Simplify command description ([3b3f7c7](3b3f7c7a7a))
2023-08-28 18:18:56 +00:00
oSumAtrIX
3b3f7c7a7a feat: Simplify command description 2023-08-28 20:17:12 +02:00
oSumAtrIX
bbb1a63abd fix: format patches input
Previously you could not use the original patches names because they were formatted but not the input
2023-08-28 20:16:00 +02:00
semantic-release-bot
f7fcbc55bb chore(release): 3.0.2-dev.2 [skip ci]
## [3.0.2-dev.2](https://github.com/ReVanced/revanced-cli/compare/v3.0.2-dev.1...v3.0.2-dev.2) (2023-08-28)

### Bug Fixes

* check for package compatibility at first ([9fe5a0b](9fe5a0b6d9))
2023-08-28 16:38:55 +00:00
oSumAtrIX
9fe5a0b6d9 fix: check for package compatibility at first 2023-08-28 18:37:11 +02:00
semantic-release-bot
4f2d2568d3 chore(release): 3.0.2-dev.1 [skip ci]
## [3.0.2-dev.1](https://github.com/ReVanced/revanced-cli/compare/v3.0.1...v3.0.2-dev.1) (2023-08-28)

### Bug Fixes

* do not filter explicitly included patches ([a3d8f00](a3d8f004ec))
2023-08-28 15:59:00 +00:00
semantic-release-bot
a3d8f004ec fix: do not filter explicitly included patches 2023-08-28 17:56:47 +02:00
semantic-release-bot
41ffc99ad0 chore(release): 3.0.1 [skip ci]
## [3.0.1](https://github.com/ReVanced/revanced-cli/compare/v3.0.0...v3.0.1) (2023-08-28)
2023-08-28 13:23:57 +00:00
oSumAtrIX
1121376018 chore: merge branch dev to main (#254) 2023-08-28 15:21:55 +02:00
semantic-release-bot
48e1689223 chore(release): 3.0.1-dev.1 [skip ci]
## [3.0.1-dev.1](https://github.com/ReVanced/revanced-cli/compare/v3.0.0...v3.0.1-dev.1) (2023-08-28)
2023-08-28 13:18:00 +00:00
oSumAtrIX
7580f5c2fc build(Needs bump): bump dependencies
This fixes an issue with flagging the resource table with sparse incorrectly.
2023-08-28 15:14:53 +02:00
semantic-release-bot
dd6c1392eb chore(release): 3.0.0 [skip ci]
# [3.0.0](https://github.com/ReVanced/revanced-cli/compare/v2.22.0...v3.0.0) (2023-08-26)

### Bug Fixes

* also delete temporary files when uninstalling ([52c3be2](52c3be23f2))
* delete temporary files after root installation ([a3d8705](a3d8705e89))
* do not delete output file ([0f3e090](0f3e090418))
* do not use absolute path from custom AAPT2 binary option ([a9c2a5f](a9c2a5f096))
* filtration of patches malfunctioning ([2d5a7fd](2d5a7fdf1e))
* fix running commands not running ([2c7fcaf](2c7fcaf4ad))
* only check once for patch options ([11c3a6c](11c3a6cfd4))
* print original instead of kebab cased names ([5eaad33](5eaad33dc1))
* print stack trace when a patch failed ([924c1f8](924c1f80ec))
* specify correct class containing entry-point ([1fcc591](1fcc591222))
* use correct option name ([f8972ea](f8972eac3e))

* refactor!: restructure code ([07da528](07da528ce2))

### Features

* add install command ([0350b7f](0350b7f1a2))
* add options command ([9edbbf3](9edbbf3163))
* Check for missing integrations ([c93186f](c93186fb97))
* Improve command line argument descriptions ([f9cf7d2](f9cf7d21b7))
* properly make use of logging facade ([41898d7](41898d7547))
* show full package name when listing patches ([#240](https://github.com/ReVanced/revanced-cli/issues/240)) ([7174364](7174364ef8))
* use better logging text ([b0e748d](b0e748daff))
* use friendly descriptions ([3dd875d](3dd875d14c))
* use separate command to list patches ([b74213f](b74213f66e))
* use separate command to patch ([32da961](32da961d57))
* use separate command to uninstall ([c0cc909](c0cc909626))
* use simpler log ([ba758f0](ba758f00f4))

### BREAKING CHANGES

* This introduces major changes to how ReVanced CLI is used from the command line.
2023-08-26 23:23:34 +00:00
oSumAtrIX
2a3dbafd17 chore: merge branch dev to main (#236) 2023-08-27 01:21:45 +02:00
semantic-release-bot
9e39a6f8e4 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](2d5a7fdf1e))
2023-08-25 21:48:59 +00:00
oSumAtrIX
2d5a7fdf1e fix: filtration of patches malfunctioning
Apparently, you were not able to include patches explicitly
2023-08-25 23:47:18 +02:00
semantic-release-bot
be5d812dff 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](c93186fb97))
2023-08-25 00:28:57 +00:00
oSumAtrIX
c93186fb97 feat: Check for missing integrations
Check, if the integrations file exists at first.
2023-08-25 02:26:38 +02:00
semantic-release-bot
3a198052bb 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](0f3e090418))
2023-08-24 23:32:29 +00:00
oSumAtrIX
0f3e090418 fix: do not delete output file
This fixes the output file to be deleted when the option `--purge` was used.
2023-08-25 01:30:28 +02:00
semantic-release-bot
6aed946183 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](924c1f80ec))
2023-08-24 21:47:17 +00:00
oSumAtrIX
924c1f80ec fix: print stack trace when a patch failed 2023-08-24 23:45:10 +02:00
semantic-release-bot
8dd709b6ef 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)
2023-08-24 16:11:51 +00:00
oSumAtrIX
139e7facac build(Needs bump): depend on build task when publishing
This fixes the issue that no builds are generated
2023-08-24 18:09:35 +02:00
semantic-release-bot
1d26e572f7 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](52c3be23f2))
* delete temporary files after root installation ([a3d8705](a3d8705e89))
* fix running commands not running ([2c7fcaf](2c7fcaf4ad))
* only check once for patch options ([11c3a6c](11c3a6cfd4))

### Features

* add install command ([0350b7f](0350b7f1a2))
* use friendly descriptions ([3dd875d](3dd875d14c))
2023-08-24 15:53:09 +00:00
oSumAtrIX
2c7fcaf4ad fix: fix running commands not running 2023-08-24 17:51:31 +02:00
oSumAtrIX
52c3be23f2 fix: also delete temporary files when uninstalling 2023-08-24 16:54:08 +02:00
oSumAtrIX
3dd875d14c feat: use friendly descriptions 2023-08-24 16:53:31 +02:00
oSumAtrIX
0350b7f1a2 feat: add install command
This introduces a separate utility subcommand.
2023-08-24 16:50:10 +02:00
oSumAtrIX
a3d8705e89 fix: delete temporary files after root installation 2023-08-24 16:24:33 +02:00
oSumAtrIX
a536c9f815 refactor: use better identifiers 2023-08-24 16:24:18 +02:00
oSumAtrIX
11c3a6cfd4 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
2023-08-24 15:52:12 +02:00
oSumAtrIX
a5851f0c1a build: clean after building 2023-08-24 15:35:27 +02:00
oSumAtrIX
963ae3a5fa docs: add missing inline docs 2023-08-24 15:35:27 +02:00
semantic-release-bot
93f338a731 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](41898d7547))
2023-08-24 02:56:08 +00:00
oSumAtrIX
7dcf15e03b build: bump dependencies 2023-08-24 04:52:53 +02:00
oSumAtrIX
41898d7547 feat: properly make use of logging facade 2023-08-24 04:41:44 +02:00
oSumAtrIX
45dd15f679 build: do not use a daemon when building
This decreases build times
2023-08-23 16:43:57 +02:00
semantic-release-bot
c2da04a834 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)
2023-08-23 14:40:41 +00:00
oSumAtrIX
f556909b9e 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
2023-08-23 16:38:14 +02:00
semantic-release-bot
1fa3dd7b8c 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](1fcc591222))
2023-08-23 12:53:55 +00:00
oSumAtrIX
1fcc591222 fix: specify correct class containing entry-point 2023-08-23 14:51:44 +02:00
oSumAtrIX
1fd3b83d46 docs: fix incorrect title 2023-08-23 05:39:31 +02:00
oSumAtrIX
7ec9040d41 docs: remove invalid codeblock 2023-08-23 05:37:19 +02:00
semantic-release-bot
44cc88b25d 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](a9c2a5f096))
* use correct option name ([f8972ea](f8972eac3e))

* refactor!: restructure code ([07da528](07da528ce2))

### Features

* add options command ([9edbbf3](9edbbf3163))
* use better logging text ([b0e748d](b0e748daff))
* use separate command to list patches ([b74213f](b74213f66e))
* use separate command to patch ([32da961](32da961d57))
* use separate command to uninstall ([c0cc909](c0cc909626))
* use simpler log ([ba758f0](ba758f00f4))

### BREAKING CHANGES

* This introduces major changes to how ReVanced CLI is used from the command line.
2023-08-23 03:17:57 +00:00
oSumAtrIX
97548f80b3 build: fix dummy publish task 2023-08-23 05:16:17 +02:00
oSumAtrIX
d766f0e229 refactor!: restructure code (#242) 2023-08-23 04:57:46 +02:00
oSumAtrIX
b0e748daff feat: use better logging text 2023-08-23 04:46:42 +02:00
oSumAtrIX
ba758f00f4 feat: use simpler log 2023-08-23 04:32:28 +02:00
oSumAtrIX
a9c2a5f096 fix: do not use absolute path from custom AAPT2 binary option 2023-08-23 04:29:58 +02:00
oSumAtrIX
0dcd838de3 build: migrate dependencies to version catalogs 2023-08-23 04:28:12 +02:00
oSumAtrIX
a7290353bf build: make sure to add use all necessary repositories 2023-08-23 03:56:31 +02:00
oSumAtrIX
47a20afd2d docs: improve correctness 2023-08-23 03:53:24 +02:00
oSumAtrIX
f8972eac3e fix: use correct option name 2023-08-23 03:40:42 +02:00
oSumAtrIX
9edbbf3163 feat: add options command 2023-08-23 03:35:38 +02:00
oSumAtrIX
32da961d57 feat: use separate command to patch 2023-08-23 03:32:39 +02:00
oSumAtrIX
8a5daab2a3 build: add properties resource file 2023-08-23 03:30:13 +02:00
oSumAtrIX
c0cc909626 feat: use separate command to uninstall 2023-08-23 03:30:04 +02:00
oSumAtrIX
b74213f66e feat: use separate command to list patches 2023-08-22 23:41:32 +02:00
oSumAtrIX
fe75d4ab87 docs: use correct description 2023-08-22 23:41:32 +02:00
oSumAtrIX
07da528ce2 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.
2023-08-22 23:41:31 +02:00
semantic-release-bot
ef5fa9b4c9 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)
2023-08-14 02:14:18 +00:00
oSumAtrIX
fc359923e3 build(Needs bump): Bump dependencies 2023-08-14 04:12:10 +02:00
semantic-release-bot
2e3cd90537 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](7174364ef8))
2023-08-13 11:44:20 +00:00
programminghoch10
7174364ef8 feat: show full package name when listing patches (#240) 2023-08-13 13:42:16 +02:00
oSumAtrIX
4c9d414228 docs: fix some mistakes 2023-08-10 02:06:13 +02:00
taku
5e73e6d2de docs: add missing prerequisites (#239)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2023-08-07 02:51:01 +02:00
semantic-release-bot
667ca3051c 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)
2023-08-03 18:41:14 +00:00
oSumAtrIX
9220642a7b build(Needs bump): Bump compatibility 2023-08-03 20:38:48 +02:00
semantic-release-bot
3f47235c64 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)
2023-08-03 02:22:52 +00:00
oSumAtrIX
83b475c2e3 build(Needs bump): Update dependencies 2023-08-03 04:21:02 +02:00
semantic-release-bot
4105b638c0 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](f9cf7d21b7))
2023-07-30 00:52:33 +00:00
oSumAtrIX
f9cf7d21b7 feat: Improve command line argument descriptions 2023-07-30 02:49:49 +02:00
oSumAtrIX
0a758d86df build(Needs bump): Bump dependencies 2023-07-30 02:49:48 +02:00
oSumAtrIX
83f99e29ec ci: Change bumping commit scope 2023-07-30 02:49:39 +02:00
semantic-release-bot
00410d54c0 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](5eaad33dc1))
2023-07-24 12:20:19 +00:00
oSumAtrIX
5eaad33dc1 fix: print original instead of kebab cased names 2023-07-24 14:18:15 +02:00
semantic-release-bot
fa85482c4a chore(release): 2.22.0 [skip ci]
# [2.22.0](https://github.com/revanced/revanced-cli/compare/v2.21.5...v2.22.0) (2023-07-11)

### Features

* use new patch naming convention ([f6c221d](f6c221d72d))
2023-07-11 03:14:04 +00:00
oSumAtrIX
caed39af7f chore: merge branch dev to main (#233) 2023-07-11 05:12:30 +02:00
oSumAtrIX
20e1fdc18d chore: add notice to move option 2023-07-11 05:11:24 +02:00
semantic-release-bot
19cd541e9a chore(release): 2.22.0-dev.1 [skip ci]
# [2.22.0-dev.1](https://github.com/revanced/revanced-cli/compare/v2.21.5...v2.22.0-dev.1) (2023-07-10)

### Features

* use new patch naming convention ([e4908c7](e4908c7105))
2023-07-11 05:06:46 +02:00
oSumAtrIX
f6c221d72d feat: use new patch naming convention 2023-07-11 05:06:46 +02:00
oSumAtrIX
9c148c96bf chore: add README file 2023-07-06 20:19:19 +02:00
oSumAtrIX
2c5b53c7e9 build: update gradle 2023-07-05 23:07:31 +02:00
semantic-release-bot
77d9cb98d1 chore(release): 2.21.5 [skip ci]
## [2.21.5](https://github.com/revanced/revanced-cli/compare/v2.21.4...v2.21.5) (2023-07-01)
2023-07-01 12:45:44 +00:00
oSumAtrIX
38677e2319 chore: merge branch dev to main (#230) 2023-07-01 14:43:45 +02:00
semantic-release-bot
e5e768fa34 chore(release): 2.21.5-dev.2 [skip ci]
## [2.21.5-dev.2](https://github.com/revanced/revanced-cli/compare/v2.21.5-dev.1...v2.21.5-dev.2) (2023-07-01)
2023-07-01 00:00:31 +00:00
oSumAtrIX
9082181ce5 build(revanced-patcher): bump version 2023-07-01 01:58:28 +02:00
oSumAtrIX
542580ecff docs: add section for patch options 2023-06-30 19:40:52 +02:00
semantic-release-bot
26112a4fd2 chore(release): 2.21.5-dev.1 [skip ci]
## [2.21.5-dev.1](https://github.com/revanced/revanced-cli/compare/v2.21.4...v2.21.5-dev.1) (2023-06-27)
2023-06-27 02:36:32 +00:00
oSumAtrIX
553fbdbb8f build(revanced-patcher): bump version 2023-06-27 04:10:34 +02:00
semantic-release-bot
be429a4770 chore(release): 2.21.4 [skip ci]
## [2.21.4](https://github.com/revanced/revanced-cli/compare/v2.21.3...v2.21.4) (2023-06-21)

### Bug Fixes

* remove duplicate options entries. ([d0fc886](d0fc886428))
2023-06-21 23:31:29 +00:00
oSumAtrIX
8c391cecce chore: merge branch dev to main (#229) 2023-06-22 01:29:31 +02:00
semantic-release-bot
bd14bdb5ea chore(release): 2.21.4-dev.1 [skip ci]
## [2.21.4-dev.1](https://github.com/revanced/revanced-cli/compare/v2.21.3...v2.21.4-dev.1) (2023-06-18)

### Bug Fixes

* remove duplicate options entries. ([d0fc886](d0fc886428))
2023-06-18 00:20:12 +00:00
oSumAtrIX
d0fc886428 fix: remove duplicate options entries. 2023-06-18 02:18:28 +02:00
semantic-release-bot
1ccbed8d17 chore(release): 2.21.3 [skip ci]
## [2.21.3](https://github.com/revanced/revanced-cli/compare/v2.21.2...v2.21.3) (2023-06-12)
2023-06-12 01:45:08 +00:00
oSumAtrIX
3b89fb7bc2 chore: merge branch dev to main (#228) 2023-06-12 03:43:20 +02:00
oSumAtrIX
16bb1a60fc ci: add cache step 2023-06-12 02:33:21 +02:00
oSumAtrIX
5f1843771e ci: build before running semantic-release 2023-06-12 01:52:53 +02:00
oSumAtrIX
9de90703c8 ci: remove unnecessary steps 2023-06-12 01:49:18 +02:00
semantic-release-bot
1201f9edc5 chore(release): 2.21.3-dev.1 [skip ci]
## [2.21.3-dev.1](https://github.com/revanced/revanced-cli/compare/v2.21.2...v2.21.3-dev.1) (2023-06-07)
2023-06-07 01:48:39 +00:00
oSumAtrIX
9c8f9608b2 build(revanced-patcher): bump version 2023-06-07 03:46:32 +02:00
semantic-release-bot
be90d2e360 chore(release): 2.21.2 [skip ci]
## [2.21.2](https://github.com/revanced/revanced-cli/compare/v2.21.1...v2.21.2) (2023-05-24)
2023-05-24 19:19:03 +00:00
oSumAtrIX
79dc99aa9b chore: merge branch dev to main (#225) 2023-05-24 21:17:06 +02:00
semantic-release-bot
260ff00951 chore(release): 2.21.2-dev.2 [skip ci]
## [2.21.2-dev.2](https://github.com/revanced/revanced-cli/compare/v2.21.2-dev.1...v2.21.2-dev.2) (2023-05-15)
2023-05-24 01:57:56 +02:00
LisoUseInAIKyrios
cfe4c5ed0e build(revanced-patcher): bump version (#226) 2023-05-24 01:57:56 +02:00
oSumAtrIX
7133242691 build: use Java SDK 17 for building 2023-05-09 07:13:17 +02:00
45 changed files with 1521 additions and 915 deletions

9
.gitattributes vendored Normal file
View File

@@ -0,0 +1,9 @@
#
# https://help.github.com/articles/dealing-with-line-endings/
#
# Linux start script should use lf
/gradlew text eol=lf
# These are Windows script files and should use crlf
*.bat text eol=crlf

View File

@@ -23,17 +23,21 @@ jobs:
# https://github.com/cycjimmy/semantic-release-action#private-packages
persist-credentials: false
fetch-depth: 0
- name: Setup JDK
uses: actions/setup-java@v3
- name: Cache
uses: actions/cache@v3
with:
java-version: '11'
distribution: 'zulu'
cache: gradle
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: "18"
cache: 'npm'
path: |
${{ runner.home }}/.gradle/caches
${{ runner.home }}/.gradle/wrapper
.gradle
build
node_modules
key: ${{ runner.os }}-gradle-npm-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'package-lock.json') }}
- name: Build
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# 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

View File

@@ -10,7 +10,7 @@
[
"@semantic-release/commit-analyzer", {
"releaseRules": [
{ "type": "build", "scope": "revanced-patcher", "release": "patch" }
{ "type": "build", "scope": "Needs bump", "release": "patch" }
]
}
],

View File

@@ -1,3 +1,245 @@
# [3.1.0](https://github.com/ReVanced/revanced-cli/compare/v3.0.1...v3.1.0) (2023-08-31)
### Bug Fixes
* check for package compatibility at first ([9fe5a0b](https://github.com/ReVanced/revanced-cli/commit/9fe5a0b6d93304f630436ed0e954723d9a27b0f6))
* do not filter explicitly included patches ([a3d8f00](https://github.com/ReVanced/revanced-cli/commit/a3d8f004ec405f696d99d96c74ca41b573ecf425))
* format patches input ([bbb1a63](https://github.com/ReVanced/revanced-cli/commit/bbb1a63abd80dcbecdcf362158c0429cf3e6318f))
### Features
* Simplify command description ([3b3f7c7](https://github.com/ReVanced/revanced-cli/commit/3b3f7c7a7a7b2795e3d1fad776f6b457f2e68c7b))
# [3.1.0-dev.1](https://github.com/ReVanced/revanced-cli/compare/v3.0.2-dev.2...v3.1.0-dev.1) (2023-08-28)
### Bug Fixes
* format patches input ([bbb1a63](https://github.com/ReVanced/revanced-cli/commit/bbb1a63abd80dcbecdcf362158c0429cf3e6318f))
### Features
* Simplify command description ([3b3f7c7](https://github.com/ReVanced/revanced-cli/commit/3b3f7c7a7a7b2795e3d1fad776f6b457f2e68c7b))
## [3.0.2-dev.2](https://github.com/ReVanced/revanced-cli/compare/v3.0.2-dev.1...v3.0.2-dev.2) (2023-08-28)
### Bug Fixes
* check for package compatibility at first ([9fe5a0b](https://github.com/ReVanced/revanced-cli/commit/9fe5a0b6d93304f630436ed0e954723d9a27b0f6))
## [3.0.2-dev.1](https://github.com/ReVanced/revanced-cli/compare/v3.0.1...v3.0.2-dev.1) (2023-08-28)
### Bug Fixes
* do not filter explicitly included patches ([a3d8f00](https://github.com/ReVanced/revanced-cli/commit/a3d8f004ec405f696d99d96c74ca41b573ecf425))
## [3.0.1](https://github.com/ReVanced/revanced-cli/compare/v3.0.0...v3.0.1) (2023-08-28)
## [3.0.1-dev.1](https://github.com/ReVanced/revanced-cli/compare/v3.0.0...v3.0.1-dev.1) (2023-08-28)
# [3.0.0](https://github.com/ReVanced/revanced-cli/compare/v2.22.0...v3.0.0) (2023-08-26)
### 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))
* do not delete output file ([0f3e090](https://github.com/ReVanced/revanced-cli/commit/0f3e090418771e951dfd15e5c193421f72cbe459))
* do not use absolute path from custom AAPT2 binary option ([a9c2a5f](https://github.com/ReVanced/revanced-cli/commit/a9c2a5f096627dbbf8ab1b8da26fb14529ce6bc3))
* filtration of patches malfunctioning ([2d5a7fd](https://github.com/ReVanced/revanced-cli/commit/2d5a7fdf1eb2e13f5013a790b03f09851b167fe0))
* 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))
* print original instead of kebab cased names ([5eaad33](https://github.com/ReVanced/revanced-cli/commit/5eaad33dc1fbd24c36e1498f04e21d068e85f53e))
* print stack trace when a patch failed ([924c1f8](https://github.com/ReVanced/revanced-cli/commit/924c1f80ec0d17a3bdc07a0fb2015e44c49162e4))
* specify correct class containing entry-point ([1fcc591](https://github.com/ReVanced/revanced-cli/commit/1fcc591222ab67112f2b78174a8b94106846838c))
* 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 install command ([0350b7f](https://github.com/ReVanced/revanced-cli/commit/0350b7f1a276d9dc795b22442ba4f202855ea090))
* add options command ([9edbbf3](https://github.com/ReVanced/revanced-cli/commit/9edbbf31635603f89fc7bc5dcc6c023d4cdbb5a6))
* Check for missing integrations ([c93186f](https://github.com/ReVanced/revanced-cli/commit/c93186fb9700907e65f33442e88073783cc163de))
* Improve command line argument descriptions ([f9cf7d2](https://github.com/ReVanced/revanced-cli/commit/f9cf7d21b7f1c2f11234d604a1047b9d2b165f83))
* properly make use of logging facade ([41898d7](https://github.com/ReVanced/revanced-cli/commit/41898d7547690e3130372414515c5645e5dc2634))
* 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))
* use better logging text ([b0e748d](https://github.com/ReVanced/revanced-cli/commit/b0e748daff527ee7f417b3069882e074896fc131))
* use friendly descriptions ([3dd875d](https://github.com/ReVanced/revanced-cli/commit/3dd875d14cca488ade6d21bbd4cce0d481692134))
* 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.
# [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)
### 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)
### 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)
### 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)
### 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)
### 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)
### 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)
### 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)
### 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)
# [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)
### 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)
### Features
* use new patch naming convention ([f6c221d](https://github.com/revanced/revanced-cli/commit/f6c221d72dc43ebea00e5eba6bfa02751ae8ad77))
# [2.22.0-dev.1](https://github.com/revanced/revanced-cli/compare/v2.21.5...v2.22.0-dev.1) (2023-07-10)
### Features
* use new patch naming convention ([e4908c7](https://github.com/revanced/revanced-cli/commit/e4908c71051a535f8ce3406427cebbb0941464df))
## [2.21.5](https://github.com/revanced/revanced-cli/compare/v2.21.4...v2.21.5) (2023-07-01)
## [2.21.5-dev.2](https://github.com/revanced/revanced-cli/compare/v2.21.5-dev.1...v2.21.5-dev.2) (2023-07-01)
## [2.21.5-dev.1](https://github.com/revanced/revanced-cli/compare/v2.21.4...v2.21.5-dev.1) (2023-06-27)
## [2.21.4](https://github.com/revanced/revanced-cli/compare/v2.21.3...v2.21.4) (2023-06-21)
### Bug Fixes
* remove duplicate options entries. ([d0fc886](https://github.com/revanced/revanced-cli/commit/d0fc8864286adc2677f91a319a11a90272c1001d))
## [2.21.4-dev.1](https://github.com/revanced/revanced-cli/compare/v2.21.3...v2.21.4-dev.1) (2023-06-18)
### Bug Fixes
* remove duplicate options entries. ([d0fc886](https://github.com/revanced/revanced-cli/commit/d0fc8864286adc2677f91a319a11a90272c1001d))
## [2.21.3](https://github.com/revanced/revanced-cli/compare/v2.21.2...v2.21.3) (2023-06-12)
## [2.21.3-dev.1](https://github.com/revanced/revanced-cli/compare/v2.21.2...v2.21.3-dev.1) (2023-06-07)
## [2.21.2](https://github.com/revanced/revanced-cli/compare/v2.21.1...v2.21.2) (2023-05-24)
## [2.21.2-dev.2](https://github.com/revanced/revanced-cli/compare/v2.21.2-dev.1...v2.21.2-dev.2) (2023-05-15)
## [2.21.2-dev.1](https://github.com/revanced/revanced-cli/compare/v2.21.1...v2.21.2-dev.1) (2023-05-07)
## [2.21.1](https://github.com/revanced/revanced-cli/compare/v2.21.0...v2.21.1) (2023-05-06)

3
README.md Normal file
View File

@@ -0,0 +1,3 @@
# 💻 ReVanced CLI
Command line application to use ReVanced.

View File

@@ -1,42 +1,23 @@
plugins {
kotlin("jvm") version "1.8.10"
id("com.github.johnrengelman.shadow") version "7.1.2"
kotlin("jvm") version "1.8.20"
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()
maven {
url = uri("https://maven.pkg.github.com/revanced/revanced-patcher")
credentials {
username = githubUsername
password = githubPassword
}
}
maven { url = uri("https://jitpack.io") }
google()
}
dependencies {
implementation("org.jetbrains.kotlin:kotlin-reflect:1.8.20-RC")
implementation("app.revanced:revanced-patcher:7.1.1")
implementation("info.picocli:picocli:4.7.1")
implementation("com.github.revanced:jadb:2531a28109") // updated fork
implementation("com.android.tools.build:apksig:8.1.0-alpha09")
implementation("org.bouncycastle:bcpkix-jdk15on:1.70")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.14.+")
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)
}
kotlin { jvmToolchain(11) }
tasks {
test {
@@ -45,12 +26,14 @@ tasks {
events("PASSED", "SKIPPED", "FAILED")
}
}
build {
dependsOn(shadowJar)
processResources {
expand("projectVersion" to project.version)
}
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:.*"))
@@ -58,6 +41,11 @@ tasks {
exclude(dependency("app.revanced:.*"))
}
}
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

View File

@@ -1,15 +1,17 @@
# 💼 Prerequisites
To use ReVanced CLI, you will need to fulfill certain requirements.
To use ReVanced CLI, you will need to fulfil specific requirements.
## 🤝 Requirements
- [Android Debug Bridge (adb)](https://developer.android.com/studio/command-line/adb) if you want to deploy the patched APK file on your device
- Java SDK 11 (Azul Zulu JDK or OpenJDK)
- [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)
- Java SDK 11 (Azul JDK or OpenJDK)
- 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 following section will show you how to use ReVanced CLI.
Continue: [🛠️ Using ReVanced CLI](1_usage.md)
Continue: [🛠️ Using ReVanced CLI](1_usage.md)

View File

@@ -2,73 +2,110 @@
Learn how to ReVanced CLI.
## ⚡ Setup (optional)
## ⚡ Setup ADB
1. Make sure your device is connected
1. Ensure that ADB is working
```bash
adb shell exit
```
If you plan to use the root variant, check if you have root access
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. Copy the ADB device name
2. Get your device's serial
```bash
adb devices
```
## 🔨 ReVanced CLI Usage
## 🔨 Using ReVanced CLI
- ### Show all available options for ReVanced CLI
- ### ⚙️ Show all available options for ReVanced CLI
```bash
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
java -jar revanced-cli.jar list-patches \
--with-packages \
--with-versions \
--with-options \
revanced-patches.jar [<patch-bundle> ...]
```
- ### Use ReVanced CLI without root permissions
- ### ⚙️ Generate options from patches using ReVanced CLI
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 \
revanced-patches.jar [<patch-bundle> ...]
```
> **Note**: A default `options.json` file will be automatically generated, if it does not exist
without any need for intervention when using the `patch` command.
- ### 💉 Use ReVanced CLI to patch an APK file but install without root permissions
This will install the patched APK file regularly on your device.
```bash
java -jar revanced-cli.jar \
java -jar revanced-cli.jar patch \
--patch-bundle revanced-patches.jar \
--out output.apk \
--device-serial <device-serial> \
input.apk
```
- ### 👾 Use ReVanced CLI to patch an APK file but install with root permissions
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 patch \
--patch-bundle revanced-patches.jar \
--include some-other-patch \
--exclude some-patch \
--out patched-output.apk \
--device-serial <device-serial> \
--mount \
input.apk
```
> **Note**: Some patches may require integrations
such as [ReVanced Integrations](https://github.com/revanced/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 utility uninstall \
--package-name <package-name> \
<device-serial>
```
> **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 \
-o patched-output.apk \
-b revanced-patches.jar
<device-serial>
```
- ### Mount the patched application with root permissions over the installed application
```bash
adb install input.apk # make sure the same version is installed
java -jar revanced-cli.jar \
-a input.apk \
-d device-name \
-o patched-output.apk \
-b revanced-patches.jar \
-e vanced-microg-support \
--mount
```
> **Note**:
>
> - If you want to exclude patches, you can use the option `-e`. In the case of YouTube, you can exclude
the `vanced-microg-support` patch from [ReVanced Patches](https://github.com/revanced/revanced-patches) with the
option `-e vanced-microg-support` when mounting for example.
>
> - Some patches from [ReVanced Patches](https://github.com/revanced/revanced-patches) also might require
[ReVanced Integrations](https://github.com/revanced/revanced-integrations). Supply them with the option `-m`.
> The integrations will be merged, if necessary automatically, if supplied.
>
> - If you supplied a device with the option `-d`, the patched application will be automatically installed on the
device.
> **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`.

View File

@@ -1,2 +1,4 @@
org.gradle.parallel = true
org.gradle.caching = true
kotlin.code.style = official
version = 2.21.2-dev.1
version = 3.1.0

25
gradle/libs.versions.toml Normal file
View File

@@ -0,0 +1,25 @@
[versions]
shadow = "8.1.1"
apksig = "8.1.1"
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.2.1"
[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" }

Binary file not shown.

View File

@@ -1,5 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

293
gradlew vendored
View File

@@ -1,7 +1,7 @@
#!/usr/bin/env sh
#!/bin/sh
#
# Copyright 2015 the original author or authors.
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,67 +17,98 @@
#
##############################################################################
##
## Gradle start up script for UN*X
##
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
MAX_FD=maximum
warn () {
echo "$*"
}
} >&2
die () {
echo
echo "$*"
echo
exit 1
}
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MSYS* | MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@@ -87,9 +118,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD="$JAVA_HOME/bin/java"
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -98,88 +129,120 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

15
gradlew.bat vendored
View File

@@ -14,7 +14,7 @@
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@@ -25,7 +25,8 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal

View File

@@ -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"

View File

@@ -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
)
}
}
}

View File

@@ -0,0 +1,91 @@
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
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..*"
)
private lateinit var patchBundles: Array<File>
@Option(
names = ["-d", "--with-descriptions"], description = ["List their descriptions"], showDefaultValue = ALWAYS
)
private var withDescriptions: Boolean = true
@Option(
names = ["-p", "--with-packages"],
description = ["List the packages the patches are compatible with"],
showDefaultValue = ALWAYS
)
private var withPackages: Boolean = false
@Option(
names = ["-v", "--with-versions"],
description = ["List the versions of the apps the patches are compatible with"],
showDefaultValue = ALWAYS
)
private var withVersions: Boolean = false
@Option(
names = ["-o", "--with-options"], description = ["List the options of the patches"], showDefaultValue = ALWAYS
)
private 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")
)
}
}
logger.info(PatchBundleLoader.Jar(*patchBundles).joinToString("\n\n") { it.buildString() })
}
}

View File

@@ -1,264 +1,67 @@
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.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.patchName
import app.revanced.patcher.patch.Patch
import app.revanced.patcher.util.patch.PatchBundle
import app.revanced.utils.Options
import app.revanced.utils.Options.setOptions
import app.revanced.utils.adb.Adb
import picocli.CommandLine.*
import java.io.File
import java.nio.file.Files
import app.revanced.cli.command.utility.UtilityCommand
import app.revanced.patcher.patch.PatchClass
import picocli.CommandLine
import picocli.CommandLine.Command
import picocli.CommandLine.IVersionProvider
import java.util.*
import java.util.logging.*
/**
* Alias for return type of [PatchBundle.loadPatches].
*/
internal typealias PatchList = List<Class<out Patch<Context>>>
private class CLIVersionProvider : IVersionProvider {
override fun getVersion() = arrayOf(
MainCommand::class.java.`package`.implementationVersion ?: "unknown"
)
fun main(args: Array<String>) {
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).toByteArray().let {
if (record.level.intValue() > Level.INFO.intValue())
System.err.write(it)
else
System.out.write(it)
}
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<PatchClass>
private object CLIVersionProvider : IVersionProvider {
override fun getVersion(): Array<String> {
Properties().apply {
load(MainCommand::class.java.getResourceAsStream("/app/revanced/cli/version.properties"))
}.let {
return arrayOf("ReVanced CLI v${it.getProperty("version")}")
}
}
}
@Command(
name = "ReVanced-CLI",
name = "revanced-cli",
description = ["Command line application to use ReVanced"],
mixinStandardHelpOptions = true,
versionProvider = CLIVersionProvider::class
versionProvider = CLIVersionProvider::class,
subcommands = [
ListPatchesCommand::class,
PatchCommand::class,
OptionsCommand::class,
UtilityCommand::class,
]
)
internal object MainCommand : Runnable {
val logger = DefaultCliLogger()
@ArgGroup(exclusive = false, multiplicity = "1")
lateinit var args: Args
class Args {
@Option(names = ["-a", "--apk"], description = ["Input 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 = ["-d", "--deploy-on"],
description = ["If specified, deploy to device over ADB with given name"]
)
var deploy: String? = null
@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<String>()
@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 patchingArgs: PatchingArgs? = null
}
class ListingArgs {
@Option(names = ["-l", "--list"], description = ["List patches"], required = true)
var listOnly: Boolean = false
@Option(names = ["--with-versions"], description = ["List patches with compatible versions"])
var withVersions: Boolean = false
@Option(names = ["--with-packages"], description = ["List patches with compatible packages"])
var withPackages: Boolean = false
}
class PatchingArgs {
@Option(names = ["-o", "--out"], description = ["Output file path"], required = true)
lateinit var outputPath: String
@Option(names = ["-e", "--exclude"], description = ["Explicitly exclude patches"])
var excludedPatches = arrayOf<String>()
@Option(
names = ["--exclusive"],
description = ["Only installs the patches you include, not including any patch by default"]
)
var exclusive = false
@Option(names = ["-i", "--include"], description = ["Include patches"])
var includedPatches = arrayOf<String>()
@Option(names = ["--experimental"], description = ["Disable patch version compatibility patch"])
var experimental: Boolean = false
@Option(names = ["-m", "--merge"], description = ["One or more dex file containers to merge"])
var mergeFiles = listOf<File>()
@Option(names = ["--mount"], description = ["If specified, instead of installing, mount"])
var mount: Boolean = false
@Option(names = ["--cn"], description = ["Overwrite the default CN for the signed file"])
var cn = "ReVanced"
@Option(names = ["--keystore"], description = ["File path to your keystore"])
var keystorePath: String? = null
@Option(names = ["-p", "--password"], description = ["Overwrite the default password for the signed file"])
var password = "ReVanced"
@Option(names = ["-t", "--temp-dir"], description = ["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"]
)
var clean: Boolean = false
@Option(names = ["--custom-aapt2-binary"], description = ["Path to custom aapt2 binary"])
var aaptPath: String = ""
}
override fun run() {
if (args.patchArgs?.listingArgs?.listOnly == true) return printListOfPatches()
if (args.uninstall) return uninstall()
val pArgs = this.args.patchArgs?.patchingArgs ?: return
val outputFile = File(pArgs.outputPath) // the file to write to
val allPatches = args.patchArgs!!.patchBundles.flatMap { bundle ->
PatchBundle.Jar(bundle).loadPatches()
}
args.patchArgs!!.optionsFile.let {
if (it.exists()) allPatches.setOptions(it, logger)
else Options.serialize(allPatches, prettyPrint = true).let(it::writeText)
}
val patcher = app.revanced.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,
PatcherLogger
)
)
// prepare adb
val adb: Adb? = args.deploy?.let {
Adb(outputFile, patcher.context.packageMetadata.packageName, args.deploy!!, !pArgs.mount)
}
// start the patcher
val result = Patcher.start(patcher, allPatches)
val cacheDirectory = File(pArgs.cacheDirectory)
// align the file
val alignedFile = cacheDirectory.resolve("${outputFile.nameWithoutExtension}_aligned.apk")
Aligning.align(result, args.inputFile, alignedFile)
// sign the file
val finalFile = if (!pArgs.mount) {
val signedOutput = cacheDirectory.resolve("${outputFile.nameWithoutExtension}_signed.apk")
Signing.sign(
alignedFile,
signedOutput,
SigningOptions(
pArgs.cn,
pArgs.password,
pArgs.keystorePath ?: outputFile.absoluteFile.parentFile
.resolve("${outputFile.nameWithoutExtension}.keystore")
.canonicalPath
)
)
signedOutput
} else
alignedFile
// finally copy to the specified output file
logger.info("Copying ${finalFile.name} to ${outputFile.name}")
finalFile.copyTo(outputFile, overwrite = true)
// clean up the cache directory if needed
if (pArgs.clean)
cleanUp(pArgs.cacheDirectory)
// deploy if specified
adb?.deploy()
if (pArgs.clean && args.deploy != null) Files.delete(outputFile.toPath())
logger.info("Finished")
}
private fun cleanUp(cacheDirectory: String) {
val result = if (File(cacheDirectory).deleteRecursively())
"Cleaned up cache directory"
else
"Failed to clean up cache directory"
logger.info(result)
}
private fun uninstall() {
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()
}
private fun printListOfPatches() {
val logged = mutableListOf<String>()
for (patchBundlePath in args.patchArgs?.patchBundles!!) for (patch in PatchBundle.Jar(patchBundlePath)
.loadPatches()) {
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.substringAfterLast(".").padStart(10)
append(packageName)
append("\t")
}
// Add patch name
val patchName = patch.patchName.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 object MainCommand

View File

@@ -0,0 +1,46 @@
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
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..*"
)
private lateinit var patchBundles: Array<File>
@CommandLine.Option(
names = ["-p", "--path"], description = ["Path to patch options JSON file"], showDefaultValue = ALWAYS
)
private var path: File = File("options.json")
@CommandLine.Option(
names = ["-o", "--overwrite"], description = ["Overwrite existing options file"], showDefaultValue = ALWAYS
)
private var overwrite: Boolean = false
@CommandLine.Option(
names = ["-u", "--update"],
description = ["Update existing options by adding missing and removing non-existent options"],
showDefaultValue = ALWAYS
)
private var update: Boolean = false
override fun run() = if (!path.exists() || overwrite) with(PatchBundleLoader.Jar(*patchBundles)) {
if (update) setOptions(path)
Options.serialize(this, prettyPrint = true).let(path::writeText)
}
else logger.severe("Options file already exists, use --override to override it")
}

View File

@@ -0,0 +1,349 @@
package app.revanced.cli.command
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
import java.io.PrintWriter
import java.io.StringWriter
import java.util.logging.Logger
@CommandLine.Command(
name = "patch", description = ["Patch an APK file"]
)
internal object PatchCommand : Runnable {
private val logger = Logger.getLogger(PatchCommand::class.java.name)
@CommandLine.Parameters(
description = ["APK file to be patched"], arity = "1..1"
)
private lateinit var apk: File
@CommandLine.Option(
names = ["-b", "--patch-bundle"], description = ["One or more bundles of patches"], required = true
)
private var patchBundles = emptyList<File>()
@CommandLine.Option(
names = ["-m", "--merge"], description = ["One or more DEX files or containers to merge into the APK"]
)
private var integrations = listOf<File>()
@CommandLine.Option(
names = ["-i", "--include"], description = ["List of patches to include"]
)
private var includedPatches = arrayOf<String>()
@CommandLine.Option(
names = ["-e", "--exclude"], description = ["List of patches to exclude"]
)
private var excludedPatches = arrayOf<String>()
@CommandLine.Option(
names = ["--options"], description = ["Path to patch options JSON file"], showDefaultValue = ALWAYS
)
private var optionsFile: File = File("options.json")
@CommandLine.Option(
names = ["--exclusive"],
description = ["Only include patches that are explicitly specified to be included"],
showDefaultValue = ALWAYS
)
private var exclusive = false
@CommandLine.Option(
names = ["-f","--force"],
description = ["Force inclusion of patches that are incompatible with the supplied APK file's version"],
showDefaultValue = ALWAYS
)
private var force: Boolean = false
@CommandLine.Option(
names = ["-o", "--out"], description = ["Path to save the patched APK file to"], required = true
)
private lateinit var outputFilePath: File
@CommandLine.Option(
names = ["-d", "--device-serial"], description = ["ADB device serial to install to"], showDefaultValue = ALWAYS
)
private var deviceSerial: String? = null
@CommandLine.Option(
names = ["--mount"], description = ["Install by mounting the patched APK file"], showDefaultValue = ALWAYS
)
private var mount: Boolean = false
@CommandLine.Option(
names = ["--common-name"],
description = ["The common name of the signer of the patched APK file"],
showDefaultValue = ALWAYS
)
private var commonName = "ReVanced"
@CommandLine.Option(
names = ["--keystore"], description = ["Path to the keystore to sign the patched APK file with"]
)
private var keystorePath: String? = null
@CommandLine.Option(
names = ["--password"], description = ["The password of the keystore to sign the patched APK file with"]
)
private var password = "ReVanced"
@CommandLine.Option(
names = ["-r", "--resource-cache"],
description = ["Path to temporary resource cache directory"],
showDefaultValue = ALWAYS
)
private var resourceCachePath = File("revanced-resource-cache")
@CommandLine.Option(
names = ["--custom-aapt2-binary"], description = ["Path to a custom AAPT binary to compile resources with"]
)
private var aaptBinaryPath = File("")
@CommandLine.Option(
names = ["-p", "--purge"],
description = ["Purge the temporary resource cache directory after patching"],
showDefaultValue = ALWAYS
)
private var purge: Boolean = false
override fun run() {
// region Prepare
if (!apk.exists()) {
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
}
val adbManager = deviceSerial?.let { serial ->
if (mount) AdbManager.RootAdbManager(serial)
else AdbManager.UserAdbManager(serial)
}
// endregion
// region Load patches
logger.info("Loading patches")
val patches = PatchBundleLoader.Jar(*patchBundles.toTypedArray())
val integrations = integrations
logger.info("Setting patch options")
optionsFile.let {
if (it.exists()) patches.setOptions(it)
else Options.serialize(patches, prettyPrint = true).let(it::writeText)
}
// endregion
// region Patch
val patcher = Patcher(
PatcherOptions(
apk,
resourceCachePath,
aaptBinaryPath.path,
resourceCachePath.absolutePath,
)
)
val result = patcher.apply {
acceptIntegrations(integrations)
acceptPatches(filterPatchSelection(patches))
// Execute patches.
runBlocking {
apply(false).collect { patchResult ->
patchResult.exception?.let {
StringWriter().use { writer ->
it.printStackTrace(PrintWriter(writer))
logger.severe("${patchResult.patchName} failed: $writer")
}
} ?: 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")
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)
* - [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.
*/
private fun Patcher.filterPatchSelection(patches: PatchList) = buildList {
// TODO: Remove this eventually because
// patches named "patch-name" and "patch name" will conflict.
fun String.format() = lowercase().replace(" ", "-")
val formattedExcludedPatches = excludedPatches.map { it.format() }
val formattedIncludedPatches = includedPatches.map { it.format() }
val packageName = context.packageMetadata.packageName
val packageVersion = context.packageMetadata.packageVersion
patches.forEach patch@{ patch ->
val formattedPatchName = patch.patchName.format()
val explicitlyExcluded = formattedExcludedPatches.contains(formattedPatchName)
if (explicitlyExcluded) return@patch logger.info("Excluding ${patch.patchName}")
// 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` ->
val matchesVersion = force || `package`.versions.let {
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(", ")}"
}
)
} ?: 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.")
// If the patch is implicitly included, it will be only included if [exclusive] is false.
val implicitlyIncluded = !exclusive && patch.include
// If the patch is explicitly included, it will be included even if [exclusive] is false.
val explicitlyIncluded = formattedIncludedPatches.contains(formattedPatchName)
val included = implicitlyIncluded || explicitlyIncluded
if (!included) return@patch logger.info("${patch.patchName} excluded by default") // Case 1.
logger.fine("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")
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)
}
}

View File

@@ -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<String>
@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())
}
}

View File

@@ -0,0 +1,40 @@
package app.revanced.cli.command.utility
import app.revanced.utils.adb.AdbManager
import picocli.CommandLine.*
import picocli.CommandLine.Help.Visibility.ALWAYS
import java.util.logging.Logger
@Command(
name = "uninstall",
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)
@Parameters(description = ["ADB device serials"], arity = "1..*")
private lateinit var deviceSerials: Array<String>
@Option(names = ["-p", "--package-name"], description = ["Package name of the app to uninstall"], required = true)
private lateinit var packageName: String
@Option(
names = ["-u", "--unmount"],
description = ["Uninstall by unmounting the patched APK file"],
showDefaultValue = ALWAYS
)
private var unmount: Boolean = false
override fun run() = try {
deviceSerials.forEach { deviceSerial ->
if (unmount) {
AdbManager.RootAdbManager(deviceSerial)
} else {
AdbManager.UserAdbManager(deviceSerial)
}.uninstall(packageName)
}
} catch (e: AdbManager.DeviceNotFoundException) {
logger.severe(e.toString())
}
}

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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()
}
}

View File

@@ -1,8 +0,0 @@
package app.revanced.cli.main
import app.revanced.cli.command.MainCommand
import picocli.CommandLine
internal fun main(args: Array<String>) {
CommandLine(MainCommand).execute(*args)
}

View File

@@ -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()
}
}

View File

@@ -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)
}

View File

@@ -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)
}
}

View File

@@ -1,15 +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()
/**
@@ -25,9 +28,12 @@ internal object Options {
.map { patch ->
PatchOption(
patch.patchName,
patch.options!!.map { option -> PatchOption.Option(option.key, option.value) }
patch.options!!.map { option -> Option(option.key, option.value) }
)
}.let {
}
// See https://github.com/revanced/revanced-patches/pull/2434/commits/60e550550b7641705e81aa72acfc4faaebb225e7.
.distinctBy { it.patchName }
.let {
if (prettyPrint)
mapper.writerWithDefaultPrettyPrinter().writeValueAsString(it)
else
@@ -49,22 +55,24 @@ 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
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?.warn("${patch.patchName} has no options")
?: run{
logger.warning("${patch.patchName} has no options")
return@patch
}
} catch (e: NoSuchOptionException) {
logger?.error(e.message ?: "Unknown error")
logger.info(e.toString())
}
}
}
@@ -76,10 +84,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.

View File

@@ -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 modeInstall: 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 (!modeInstall && 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 (modeInstall) {
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()
}
}

View File

@@ -0,0 +1,140 @@
package app.revanced.utils.adb
import app.revanced.utils.adb.AdbManager.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
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) : 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.fine("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.fine("Closed")
}
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")
val applyReplacement = getPlaceholderReplacement(
apk.packageName ?: throw IllegalArgumentException("Package name is required")
)
device.push(apk.file, TMP_PATH)
device.run("$CREATE_DIR $INSTALLATION_PATH")
device.run(INSTALL_PATCHED_APK.applyReplacement())
device.createFile(TMP_PATH, MOUNT_SCRIPT.applyReplacement())
device.run(INSTALL_MOUNT.applyReplacement())
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)
}
override fun uninstall(packageName: String) {
logger.info("Uninstalling $packageName by unmounting")
val applyReplacement = getPlaceholderReplacement(packageName)
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)
}
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) : AdbManager(deviceSerial) {
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.
* @param packageName The package name of 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")
}

View File

@@ -5,10 +5,9 @@ import se.vidstige.jadb.RemoteFile
import se.vidstige.jadb.ShellProcessBuilder
import java.io.File
internal fun JadbDevice.buildCommand(command: String, su: Boolean = true): ShellProcessBuilder {
if (su) {
return shellProcessBuilder("su -c \'$command\'")
}
if (su) return shellProcessBuilder("su -c \'$command\'")
val args = command.split(" ") as ArrayList<String>
val cmd = args.removeFirst()
@@ -20,10 +19,15 @@ internal fun JadbDevice.run(command: String, su: Boolean = true): Int {
return this.buildCommand(command, su).start().waitFor()
}
internal fun JadbDevice.copy(targetPath: String, file: File) {
push(file, RemoteFile(targetPath))
}
internal fun JadbDevice.hasSu() =
this.startCommand("su -h", false).waitFor() == 0
internal fun JadbDevice.createFile(targetFile: String, content: String) {
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))
}
private fun JadbDevice.startCommand(command: String, su: Boolean) =
shellProcessBuilder(if (su) "su -c '$command'" else command).start()

View File

@@ -1,54 +1,37 @@
package app.revanced.utils.adb
internal object Constants {
// template placeholder to replace a string in commands
internal const val PLACEHOLDER = "TEMPLATE_PACKAGE_NAME"
internal const val PLACEHOLDER = "PLACEHOLDER"
// 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)"
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"
// default mount file name
private const val NAME_MOUNT_SCRIPT = "mount_revanced_$PLACEHOLDER.sh"
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)"
// initial directory to push files to via adb push
internal const val PATH_INIT_PUSH = "/data/local/tmp/revanced.delete"
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"
// 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"
// mount script path
internal const val PATH_MOUNT = "/data/adb/service.d/$NAME_MOUNT_SCRIPT"
// 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 =
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"
// 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 INSTALL_MOUNT = "mv $TMP_PATH $MOUNT_PATH && chmod +x $MOUNT_PATH"
// mount script
internal val CONTENT_MOUNT_SCRIPT =
internal 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_REVANCED_APP"
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

View File

@@ -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

View File

@@ -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

View File

@@ -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<ZipEntry> = mutableListOf()
private var entries: MutableList<ZipEntry> = 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()
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -1,76 +0,0 @@
package app.revanced.utils.patcher
import app.revanced.cli.command.MainCommand
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<Class<out Patch<Context>>>()
allPatches.forEach patchLoop@{ patch ->
val compatiblePackages = patch.compatiblePackages
val patchName = patch.patchName
val args = MainCommand.args.patchArgs?.patchingArgs!!
val prefix = "Skipping $patchName"
if (compatiblePackages == null) logger.trace("$patchName: No constraint on packages.")
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
}
}
if (args.excludedPatches.contains(patchName)) {
logger.info("$prefix: Manually excluded")
return@patchLoop
} else if ((!patch.include || args.exclusive) && !args.includedPatches.contains(patchName)) {
logger.info("$prefix: Excluded by default")
return@patchLoop
}
logger.trace("Adding $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")
}
}

View File

@@ -1,7 +1,5 @@
package app.revanced.utils.signing
import app.revanced.cli.command.MainCommand.logger
import app.revanced.cli.signing.SigningOptions
import com.android.apksig.ApkSigner
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
@@ -17,11 +15,44 @@ import java.math.BigInteger
import java.security.*
import java.security.cert.X509Certificate
import java.util.*
import java.util.logging.Logger
internal class Signer(
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()
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 +81,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
}
}

View File

@@ -1,4 +1,4 @@
package app.revanced.cli.signing
package app.revanced.utils.signing
data class SigningOptions(
val cn: String,

View File

@@ -0,0 +1 @@
version=${projectVersion}

View File

@@ -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<out Patch<Context>>)
private var patches = listOf(PatchOptionsTestPatch::class.java as Class<out Patch<Context<*>>>)
@Test
@Order(1)