chore: Merge branch dev to main (#299)

This commit is contained in:
oSumAtrIX 2023-11-26 06:02:21 +01:00 committed by GitHub
commit 41bdb04ab0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 7229 additions and 2238 deletions

View File

@ -6,12 +6,73 @@ body:
- type: markdown - type: markdown
attributes: attributes:
value: | value: |
<p align="center">
<picture>
<source
width="256px"
media="(prefers-color-scheme: dark)"
srcset="https://raw.githubusercontent.com/revanced/revanced-cli/main/assets/revanced-headline/revanced-headline-vertical-dark.svg"
>
<img
width="256px"
src="https://raw.githubusercontent.com/revanced/revanced-cli/main/assets/revanced-headline/revanced-headline-vertical-light.svg"
>
</picture>
<br>
<a href="https://revanced.app/">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/revanced/revanced-cli/main/assets/revanced-logo/revanced-logo.svg" />
<img height="24px" src="https://raw.githubusercontent.com/revanced/revanced-cli/main/assets/revanced-logo/revanced-logo.svg" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://github.com/ReVanced">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
<img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="http://revanced.app/discord">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://reddit.com/r/revancedapp">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://t.me/app_revanced">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://x.com/revancedapp">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/93124920/270180600-7c1b38bf-889b-4d68-bd5e-b9d86f91421a.png">
<img height="24px" src="https://user-images.githubusercontent.com/93124920/270108715-d80743fa-b330-4809-b1e6-79fbdc60d09c.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://www.youtube.com/@ReVanced">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
</picture>
</a>
<br>
<br>
Continuing the legacy of Vanced
</p>
# ReVanced CLI bug report # ReVanced CLI bug report
Please check for existing bug reports Before creating a new bug report, please keep the following in mind:
[here](https://github.com/ReVanced/revanced-cli/labels/Bug%20report)
before creating a new one.
- **Do not submit a duplicate bug report**: You can review existing bug reports [here](https://github.com/ReVanced/revanced-cli/labels/Bug%20report).
- **Review the contribution guidelines**: Make sure your bug report adheres to it. You can find the guidelines [here](https://github.com/ReVanced/revanced-cli/blob/main/CONTRIBUTING.md).
- **Do not use the issue page for support**: If you need help or have questions, check out other platforms on [revanced.app](https://revanced.app).
- type: textarea - type: textarea
attributes: attributes:
label: Bug description label: Bug description
@ -39,9 +100,9 @@ body:
id: acknowledgements id: acknowledgements
attributes: attributes:
label: Acknowledgements label: Acknowledgements
description: Your issue will be closed if you don't follow the checklist below. description: Your bug report will be closed if you don't follow the checklist below.
options: options:
- label: This request is not a duplicate of an existing issue. - label: This issue is not a duplicate of an existing bug report.
required: true required: true
- label: I have chosen an appropriate title. - label: I have chosen an appropriate title.
required: true required: true

View File

@ -6,12 +6,73 @@ body:
- type: markdown - type: markdown
attributes: attributes:
value: | value: |
<p align="center">
<picture>
<source
width="256px"
media="(prefers-color-scheme: dark)"
srcset="https://raw.githubusercontent.com/revanced/revanced-cli/main/assets/revanced-headline/revanced-headline-vertical-dark.svg"
>
<img
width="256px"
src="https://raw.githubusercontent.com/revanced/revanced-cli/main/assets/revanced-headline/revanced-headline-vertical-light.svg"
>
</picture>
<br>
<a href="https://revanced.app/">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/revanced/revanced-cli/main/assets/revanced-logo/revanced-logo.svg" />
<img height="24px" src="https://raw.githubusercontent.com/revanced/revanced-cli/main/assets/revanced-logo/revanced-logo.svg" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://github.com/ReVanced">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
<img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="http://revanced.app/discord">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://reddit.com/r/revancedapp">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://t.me/app_revanced">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://x.com/revancedapp">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/93124920/270180600-7c1b38bf-889b-4d68-bd5e-b9d86f91421a.png">
<img height="24px" src="https://user-images.githubusercontent.com/93124920/270108715-d80743fa-b330-4809-b1e6-79fbdc60d09c.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://www.youtube.com/@ReVanced">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
</picture>
</a>
<br>
<br>
Continuing the legacy of Vanced
</p>
# ReVanced CLI feature request # ReVanced CLI feature request
Please check for existing feature requests Before creating a new feature request, please keep the following in mind:
[here](https://github.com/ReVanced/revanced-cli/labels/Feature%20request)
before creating a new one.
- **Do not submit a duplicate feature request**: You can review existing feature requests [here](https://github.com/ReVanced/revanced-cli//labels/Feature%20request).
- **Review the contribution guidelines**: Make sure your bug report adheres to it. You can find the guidelines [here](https://github.com/ReVanced/revanced-cli//blob/main/CONTRIBUTING.md).
- **Do not use the issue page for support**: If you need help or have questions, check out other platforms on [revanced.app](https://revanced.app).
- type: textarea - type: textarea
attributes: attributes:
label: Feature description label: Feature description
@ -35,9 +96,9 @@ body:
id: acknowledgements id: acknowledgements
attributes: attributes:
label: Acknowledgements label: Acknowledgements
description: Your issue will be closed if you don't follow the checklist below. description: Your feature request will be closed if you don't follow the checklist below.
options: options:
- label: This request is not a duplicate of an existing issue. - label: This issue is not a duplicate of an existing feature request.
required: true required: true
- label: I have chosen an appropriate title. - label: I have chosen an appropriate title.
required: true required: true

2
.github/config.yml vendored
View File

@ -1,2 +1,2 @@
firstPRMergeComment: > firstPRMergeComment: >
Thank you for contributing to ReVanced. Join us on [Discord](https://revanced.app/discord) if you want to receive a contributor role. Thank you for contributing to ReVanced. Join us on [Discord](https://revanced.app/discord) to receive a role for your contribution.

View File

@ -16,6 +16,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Open pull request - name: Open pull request
uses: repo-sync/pull-request@v2 uses: repo-sync/pull-request@v2
with: with:

View File

@ -23,23 +23,26 @@ jobs:
# https://github.com/cycjimmy/semantic-release-action#private-packages # https://github.com/cycjimmy/semantic-release-action#private-packages
persist-credentials: false persist-credentials: false
fetch-depth: 0 fetch-depth: 0
- name: Cache
- name: Cache Node modules
uses: actions/cache@v3 uses: actions/cache@v3
with: with:
path: | path: |
${{ runner.home }}/.gradle/caches
${{ runner.home }}/.gradle/wrapper
.gradle
build
node_modules node_modules
key: ${{ runner.os }}-gradle-npm-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'package-lock.json') }} key: npm-${{ hashFiles('package-lock.json') }}
- name: Cache Gradle
uses: burrunan/gradle-cache-action@v1
- name: Build - name: Build
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Cleaning is necessary to avoid uploading two identical artifacts with different versions # Cleaning is necessary to avoid uploading two identical artifacts with different versions
run: ./gradlew clean --no-daemon run: ./gradlew clean --no-daemon
- name: Setup semantic-release - name: Setup semantic-release
run: npm install run: npm install
- name: Release - name: Release
env: env:
GITHUB_TOKEN: ${{ secrets.REPOSITORY_PUSH_ACCESS }} GITHUB_TOKEN: ${{ secrets.REPOSITORY_PUSH_ACCESS }}

View File

@ -1,3 +1,19 @@
# [4.2.0-dev.1](https://github.com/ReVanced/revanced-cli/compare/v4.1.1-dev.1...v4.2.0-dev.1) (2023-11-26)
### Features
* Allow selecting first Adb device, if none supplied automatically by updating dependencies ([e7c3d64](https://github.com/ReVanced/revanced-cli/commit/e7c3d64bf15bf84f3853e7ef699511bf72c13767))
* Exit application with CLI exit code ([36c6a6a](https://github.com/ReVanced/revanced-cli/commit/36c6a6a5f75f2e770a7941b3f83f430f62de13de))
* Make `--out´ option optional ([3765957](https://github.com/ReVanced/revanced-cli/commit/3765957043989fe7a8932a0c548566a78d04fc41))
## [4.1.1-dev.1](https://github.com/ReVanced/revanced-cli/compare/v4.1.0...v4.1.1-dev.1) (2023-11-25)
### Bug Fixes
* Fix typo ([#300](https://github.com/ReVanced/revanced-cli/issues/300)) ([9d96bb7](https://github.com/ReVanced/revanced-cli/commit/9d96bb7b4cc4538da416db50bd689af1a632fc45))
# [4.1.0](https://github.com/ReVanced/revanced-cli/compare/v4.0.2...v4.1.0) (2023-11-04) # [4.1.0](https://github.com/ReVanced/revanced-cli/compare/v4.0.2...v4.1.0) (2023-11-04)

View File

@ -6,34 +6,53 @@
srcset="assets/revanced-headline/revanced-headline-vertical-dark.svg" srcset="assets/revanced-headline/revanced-headline-vertical-dark.svg"
> >
<img <img
width="256px"
src="assets/revanced-headline/revanced-headline-vertical-light.svg" src="assets/revanced-headline/revanced-headline-vertical-light.svg"
> >
</picture> </picture>
<br> <br>
<a href="https://revanced.app/"> <a href="https://revanced.app/">
<img height="24px" src="assets/revanced-logo/revanced-logo-round.svg" /> <picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="assets/revanced-logo/revanced-logo.svg" />
<img height="24px" src="assets/revanced-logo/revanced-logo.svg" />
</picture>
</a>&nbsp;&nbsp;&nbsp; </a>&nbsp;&nbsp;&nbsp;
<a href="https://github.com/revanced"> <a href="https://github.com/ReVanced">
<picture> <picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" /> <source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
<img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" /> <img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
</picture> </picture>
</a>&nbsp;&nbsp;&nbsp; </a>&nbsp;&nbsp;&nbsp;
<a href="http://revanced.app/discord"> <a href="http://revanced.app/discord">
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" /> <picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp; </a>&nbsp;&nbsp;&nbsp;
<a href="https://reddit.com/r/revancedapp"> <a href="https://reddit.com/r/revancedapp">
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" /> <picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp; </a>&nbsp;&nbsp;&nbsp;
<a href="https://t.me/app_revanced"> <a href="https://t.me/app_revanced">
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" /> <picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp; </a>&nbsp;&nbsp;&nbsp;
<a href="https://twitter.com/revancedapp"> <a href="https://x.com/revancedapp">
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032018-6da37214-7474-4641-a1da-7af7db3a31cd.png" /> <picture>
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/93124920/270180600-7c1b38bf-889b-4d68-bd5e-b9d86f91421a.png">
<img height="24px" src="https://user-images.githubusercontent.com/93124920/270108715-d80743fa-b330-4809-b1e6-79fbdc60d09c.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp; </a>&nbsp;&nbsp;&nbsp;
<a href="https://www.youtube.com/@ReVanced"> <a href="https://www.youtube.com/@ReVanced">
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" /> <picture>
</a>&nbsp;&nbsp;&nbsp; <source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
</picture>
</a>
<br> <br>
<br> <br>
Continuing the legacy of Vanced Continuing the legacy of Vanced

View File

@ -6,43 +6,58 @@
srcset="assets/revanced-headline/revanced-headline-vertical-dark.svg" srcset="assets/revanced-headline/revanced-headline-vertical-dark.svg"
> >
<img <img
width="256px"
src="assets/revanced-headline/revanced-headline-vertical-light.svg" src="assets/revanced-headline/revanced-headline-vertical-light.svg"
> >
</picture> </picture>
<br> <br>
<a href="https://revanced.app/"> <a href="https://revanced.app/">
<img height="24px" src="assets/revanced-logo/revanced-logo-round.svg" /> <picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="assets/revanced-logo/revanced-logo.svg" />
<img height="24px" src="assets/revanced-logo/revanced-logo.svg" />
</picture>
</a>&nbsp;&nbsp;&nbsp; </a>&nbsp;&nbsp;&nbsp;
<a href="https://github.com/revanced"> <a href="https://github.com/ReVanced">
<picture> <picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" /> <source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
<img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" /> <img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
</picture> </picture>
</a>&nbsp;&nbsp;&nbsp; </a>&nbsp;&nbsp;&nbsp;
<a href="http://revanced.app/discord"> <a href="http://revanced.app/discord">
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" /> <picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp; </a>&nbsp;&nbsp;&nbsp;
<a href="https://reddit.com/r/revancedapp"> <a href="https://reddit.com/r/revancedapp">
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" /> <picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp; </a>&nbsp;&nbsp;&nbsp;
<a href="https://t.me/app_revanced"> <a href="https://t.me/app_revanced">
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" /> <picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp; </a>&nbsp;&nbsp;&nbsp;
<a href="https://x.com/revancedapp"> <a href="https://x.com/revancedapp">
<picture> <picture>
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/93124920/270180600-7c1b38bf-889b-4d68-bd5e-b9d86f91421a.png"> <source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/93124920/270180600-7c1b38bf-889b-4d68-bd5e-b9d86f91421a.png">
<img height="24px" src="https://user-images.githubusercontent.com/93124920/270108715-d80743fa-b330-4809-b1e6-79fbdc60d09c.png" /> <img height="24px" src="https://user-images.githubusercontent.com/93124920/270108715-d80743fa-b330-4809-b1e6-79fbdc60d09c.png" />
<picture/> </picture>
</a>&nbsp;&nbsp;&nbsp; </a>&nbsp;&nbsp;&nbsp;
<a href="https://www.youtube.com/@ReVanced"> <a href="https://www.youtube.com/@ReVanced">
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" /> <picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
</picture>
</a> </a>
<br> <br>
<br> <br>
Continuing the legacy of Vanced Continuing the legacy of Vanced
</p> </p>
# 💻 ReVanced CLI # 💻 ReVanced CLI
![GitHub Workflow Status (with event)](https://img.shields.io/github/actions/workflow/status/ReVanced/revanced-cli/release.yml) ![GitHub Workflow Status (with event)](https://img.shields.io/github/actions/workflow/status/ReVanced/revanced-cli/release.yml)

View File

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -73,7 +73,6 @@ ReVanced CLI is divided into the following fundamental commands:
```bash ```bash
java -jar revanced-cli.jar patch \ java -jar revanced-cli.jar patch \
--patch-bundle revanced-patches.jar \ --patch-bundle revanced-patches.jar \
--out patched-app.apk \
--device-serial <device-serial> \ --device-serial <device-serial> \
input.apk input.apk
``` ```
@ -107,7 +106,6 @@ ReVanced CLI is divided into the following fundamental commands:
--include "Some patch" \ --include "Some patch" \
--ii 123 \ --ii 123 \
--exclude "Some other patch" \ --exclude "Some other patch" \
--out patched-app.apk \
--device-serial <device-serial> \ --device-serial <device-serial> \
--mount \ --mount \
app.apk app.apk
@ -118,7 +116,7 @@ ReVanced CLI is divided into the following fundamental commands:
```bash ```bash
java -jar revanced-cli.jar utility uninstall \ java -jar revanced-cli.jar utility uninstall \
--package-name <package-name> \ --package-name <package-name> \
<device-serial> [<device-serial>]
``` ```
> [!NOTE] > [!NOTE]
@ -130,7 +128,7 @@ ReVanced CLI is divided into the following fundamental commands:
```bash ```bash
java -jar revanced-cli.jar utility install \ java -jar revanced-cli.jar utility install \
-a input.apk \ -a input.apk \
<device-serial> [<device-serial>]
``` ```
> [!NOTE] > [!NOTE]

View File

@ -1,4 +1,4 @@
org.gradle.parallel = true org.gradle.parallel = true
org.gradle.caching = true org.gradle.caching = true
kotlin.code.style = official kotlin.code.style = official
version = 4.1.0 version = 4.2.0-dev.1

View File

@ -1,10 +1,10 @@
[versions] [versions]
shadow = "8.1.1" shadow = "8.1.1"
kotlin-test = "1.8.20-RC" kotlin-test = "1.9.20"
kotlinx-coroutines-core = "1.7.3" kotlinx-coroutines-core = "1.7.3"
picocli = "4.7.3" picocli = "4.7.3"
revanced-patcher = "19.0.0" revanced-patcher = "19.0.0"
revanced-library = "1.2.0" revanced-library = "1.3.0"
[libraries] [libraries]
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin-test" } kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin-test" }

View File

@ -1,7 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
networkTimeout=10000 distributionSha256Sum=3e1af3ae886920c3ac87f7a91f816c0c7c436f276a6eefdb3da152100fef72ae
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dist

8612
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,9 @@
{ {
"devDependencies": { "devDependencies": {
"@saithodev/semantic-release-backmerge": "^3.1.0", "@saithodev/semantic-release-backmerge": "^3.2.1",
"@semantic-release/changelog": "^6.0.2", "@semantic-release/changelog": "^6.0.3",
"@semantic-release/git": "^10.0.1", "@semantic-release/git": "^10.0.1",
"gradle-semantic-release-plugin": "^1.7.6", "gradle-semantic-release-plugin": "^1.8.0",
"semantic-release": "^20.1.0" "semantic-release": "^22.0.8"
} }
} }

View File

@ -1 +1,7 @@
rootProject.name = "revanced-cli" rootProject.name = "revanced-cli"
buildCache {
local {
isEnabled = !System.getenv().containsKey("CI")
}
}

View File

@ -8,110 +8,126 @@ import picocli.CommandLine.Help.Visibility.ALWAYS
import java.io.File import java.io.File
import java.util.logging.Logger import java.util.logging.Logger
@Command(
@Command(name = "list-patches", description = ["List patches from supplied patch bundles."]) name = "list-patches",
description = ["List patches from supplied patch bundles."],
)
internal object ListPatchesCommand : Runnable { internal object ListPatchesCommand : Runnable {
private val logger = Logger.getLogger(ListPatchesCommand::class.java.name) private val logger = Logger.getLogger(ListPatchesCommand::class.java.name)
@Parameters( @Parameters(
description = ["Paths to patch bundles."], arity = "1..*" description = ["Paths to patch bundles."],
arity = "1..*",
) )
private lateinit var patchBundles: Array<File> private lateinit var patchBundles: Array<File>
@Option( @Option(
names = ["-d", "--with-descriptions"], description = ["List their descriptions."], showDefaultValue = ALWAYS names = ["-d", "--with-descriptions"],
description = ["List their descriptions."],
showDefaultValue = ALWAYS,
) )
private var withDescriptions: Boolean = true private var withDescriptions: Boolean = true
@Option( @Option(
names = ["-p", "--with-packages"], names = ["-p", "--with-packages"],
description = ["List the packages the patches are compatible with."], description = ["List the packages the patches are compatible with."],
showDefaultValue = ALWAYS showDefaultValue = ALWAYS,
) )
private var withPackages: Boolean = false private var withPackages: Boolean = false
@Option( @Option(
names = ["-v", "--with-versions"], names = ["-v", "--with-versions"],
description = ["List the versions of the apps the patches are compatible with."], description = ["List the versions of the apps the patches are compatible with."],
showDefaultValue = ALWAYS showDefaultValue = ALWAYS,
) )
private var withVersions: Boolean = false private var withVersions: Boolean = false
@Option( @Option(
names = ["-o", "--with-options"], description = ["List the options of the patches."], showDefaultValue = ALWAYS names = ["-o", "--with-options"],
description = ["List the options of the patches."],
showDefaultValue = ALWAYS,
) )
private var withOptions: Boolean = false private var withOptions: Boolean = false
@Option( @Option(
names = ["-u", "--with-universal-patches"], names = ["-u", "--with-universal-patches"],
description = ["List patches which are compatible with any app."], description = ["List patches which are compatible with any app."],
showDefaultValue = ALWAYS showDefaultValue = ALWAYS,
) )
private var withUniversalPatches: Boolean = true private var withUniversalPatches: Boolean = true
@Option( @Option(
names = ["-i", "--index"], names = ["-i", "--index"],
description = ["List the index of of each patch in relation to the supplied patch bundles."], description = ["List the index of each patch in relation to the supplied patch bundles."],
showDefaultValue = ALWAYS showDefaultValue = ALWAYS,
) )
private var withIndex: Boolean = true private var withIndex: Boolean = true
@Option( @Option(
names = ["-f", "--filter-package-name"], description = ["Filter patches by package name."] names = ["-f", "--filter-package-name"],
description = ["Filter patches by package name."],
) )
private var packageName: String? = null private var packageName: String? = null
override fun run() { override fun run() {
fun Patch.CompatiblePackage.buildString() = buildString { fun Patch.CompatiblePackage.buildString() =
if (withVersions && versions != null) {
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")
description?.let { appendLine("Description: $it") }
default?.let {
appendLine("Key: $key")
append("Default: $it")
} ?: append("Key: $key")
values?.let { values ->
appendLine("\nValid values:")
append(values.map { "${it.value} (${it.key})" }.joinToString("\n").prependIndent("\t"))
}
}
fun IndexedValue<Patch<*>>.buildString() = let { (index, patch) ->
buildString { buildString {
if (withIndex) appendLine("Index: $index") if (withVersions && versions != null) {
appendLine("Package name: $name")
append("Name: ${patch.name}") appendLine("Compatible versions:")
append(versions!!.joinToString("\n") { version -> version }.prependIndent("\t"))
if (withDescriptions) append("\nDescription: ${patch.description}") } else {
append("Package name: $name")
if (withOptions && patch.options.isNotEmpty()) {
appendLine("\nOptions:")
append(
patch.options.values.joinToString("\n\n") { option ->
option.buildString()
}.prependIndent("\t")
)
}
if (withPackages && patch.compatiblePackages != null) {
appendLine("\nCompatible packages:")
append(patch.compatiblePackages!!.joinToString("\n") {
it.buildString()
}.prependIndent("\t"))
} }
} }
}
fun Patch<*>.filterCompatiblePackages(name: String) = compatiblePackages?.any { it.name == name } fun PatchOption<*>.buildString() =
?: withUniversalPatches buildString {
appendLine("Title: $title")
description?.let { appendLine("Description: $it") }
default?.let {
appendLine("Key: $key")
append("Default: $it")
} ?: append("Key: $key")
values?.let { values ->
appendLine("\nValid values:")
append(values.map { "${it.value} (${it.key})" }.joinToString("\n").prependIndent("\t"))
}
}
fun IndexedValue<Patch<*>>.buildString() =
let { (index, patch) ->
buildString {
if (withIndex) appendLine("Index: $index")
append("Name: ${patch.name}")
if (withDescriptions) append("\nDescription: ${patch.description}")
if (withOptions && patch.options.isNotEmpty()) {
appendLine("\nOptions:")
append(
patch.options.values.joinToString("\n\n") { option ->
option.buildString()
}.prependIndent("\t"),
)
}
if (withPackages && patch.compatiblePackages != null) {
appendLine("\nCompatible packages:")
append(
patch.compatiblePackages!!.joinToString("\n") {
it.buildString()
}.prependIndent("\t"),
)
}
}
}
fun Patch<*>.filterCompatiblePackages(name: String) =
compatiblePackages?.any { it.name == name }
?: withUniversalPatches
val patches = PatchBundleLoader.Jar(*patchBundles).withIndex().toList() val patches = PatchBundleLoader.Jar(*patchBundles).withIndex().toList()
@ -120,4 +136,4 @@ internal object ListPatchesCommand : Runnable {
if (filtered.isNotEmpty()) logger.info(filtered.joinToString("\n\n") { it.buildString() }) if (filtered.isNotEmpty()) logger.info(filtered.joinToString("\n\n") { it.buildString() })
} }
} }

View File

@ -7,21 +7,24 @@ import picocli.CommandLine.Command
import picocli.CommandLine.IVersionProvider import picocli.CommandLine.IVersionProvider
import java.util.* import java.util.*
fun main(args: Array<String>) { fun main(args: Array<String>) {
Logger.setDefault() Logger.setDefault()
CommandLine(MainCommand).execute(*args) CommandLine(MainCommand).execute(*args).let(System::exit)
} }
private object CLIVersionProvider : IVersionProvider { private object CLIVersionProvider : IVersionProvider {
override fun getVersion() = arrayOf( override fun getVersion() =
MainCommand::class.java.getResourceAsStream( arrayOf(
"/app/revanced/cli/version.properties" MainCommand::class.java.getResourceAsStream(
)?.use { stream -> "/app/revanced/cli/version.properties",
Properties().apply { load(stream) }.let { )?.use { stream ->
"ReVanced CLI v${it.getProperty("version")}" Properties().apply {
} load(stream)
} ?: "ReVanced CLI") }.let {
"ReVanced CLI v${it.getProperty("version")}"
}
} ?: "ReVanced CLI",
)
} }
@Command( @Command(
@ -34,6 +37,6 @@ private object CLIVersionProvider : IVersionProvider {
PatchCommand::class, PatchCommand::class,
OptionsCommand::class, OptionsCommand::class,
UtilityCommand::class, UtilityCommand::class,
] ],
) )
private object MainCommand private object MainCommand

View File

@ -16,39 +16,47 @@ internal object OptionsCommand : Runnable {
private val logger = Logger.getLogger(OptionsCommand::class.java.name) private val logger = Logger.getLogger(OptionsCommand::class.java.name)
@CommandLine.Parameters( @CommandLine.Parameters(
description = ["Paths to patch bundles."], arity = "1..*" description = ["Paths to patch bundles."],
arity = "1..*",
) )
private lateinit var patchBundles: Array<File> private lateinit var patchBundles: Array<File>
@CommandLine.Option( @CommandLine.Option(
names = ["-p", "--path"], description = ["Path to patch options JSON file."], showDefaultValue = ALWAYS names = ["-p", "--path"],
description = ["Path to patch options JSON file."],
showDefaultValue = ALWAYS,
) )
private var filePath: File = File("options.json") private var filePath: File = File("options.json")
@CommandLine.Option( @CommandLine.Option(
names = ["-o", "--overwrite"], description = ["Overwrite existing options file."], showDefaultValue = ALWAYS names = ["-o", "--overwrite"],
description = ["Overwrite existing options file."],
showDefaultValue = ALWAYS,
) )
private var overwrite: Boolean = false private var overwrite: Boolean = false
@CommandLine.Option( @CommandLine.Option(
names = ["-u", "--update"], names = ["-u", "--update"],
description = ["Update existing options by adding missing and removing non-existent options."], description = ["Update existing options by adding missing and removing non-existent options."],
showDefaultValue = ALWAYS showDefaultValue = ALWAYS,
) )
private var update: Boolean = false private var update: Boolean = false
override fun run() = try { override fun run() =
PatchBundleLoader.Jar(*patchBundles).let { patches -> try {
val exists = filePath.exists() PatchBundleLoader.Jar(*patchBundles).let { patches ->
if (!exists || overwrite) { val exists = filePath.exists()
if (exists && update) patches.setOptions(filePath) if (!exists || overwrite) {
if (exists && update) patches.setOptions(filePath)
Options.serialize(patches, prettyPrint = true).let(filePath::writeText) Options.serialize(patches, prettyPrint = true).let(filePath::writeText)
} else throw OptionsFileAlreadyExistsException() } else {
throw OptionsFileAlreadyExistsException()
}
}
} catch (ex: OptionsFileAlreadyExistsException) {
logger.severe("Options file already exists, use --overwrite to override it")
} }
} catch (ex: OptionsFileAlreadyExistsException) {
logger.severe("Options file already exists, use --overwrite to override it")
}
class OptionsFileAlreadyExistsException : Exception() class OptionsFileAlreadyExistsException : Exception()
} }

View File

@ -18,9 +18,9 @@ import java.io.PrintWriter
import java.io.StringWriter import java.io.StringWriter
import java.util.logging.Logger import java.util.logging.Logger
@CommandLine.Command( @CommandLine.Command(
name = "patch", description = ["Patch an APK file."] name = "patch",
description = ["Patch an APK file."],
) )
internal object PatchCommand : Runnable { internal object PatchCommand : Runnable {
private val logger = Logger.getLogger(PatchCommand::class.java.name) private val logger = Logger.getLogger(PatchCommand::class.java.name)
@ -35,128 +35,150 @@ internal object PatchCommand : Runnable {
private var patchBundles = emptyList<File>() private var patchBundles = emptyList<File>()
@CommandLine.Option( @CommandLine.Option(
names = ["-i", "--include"], description = ["List of patches to include."] names = ["-i", "--include"],
description = ["List of patches to include."],
) )
private var includedPatches = hashSetOf<String>() private var includedPatches = hashSetOf<String>()
@CommandLine.Option( @CommandLine.Option(
names = ["--ii"], names = ["--ii"],
description = ["List of patches to include by their index in relation to the supplied patch bundles."] description = ["List of patches to include by their index in relation to the supplied patch bundles."],
) )
private var includedPatchesByIndex = arrayOf<Int>() private var includedPatchesByIndex = arrayOf<Int>()
@CommandLine.Option( @CommandLine.Option(
names = ["-e", "--exclude"], description = ["List of patches to exclude."] names = ["-e", "--exclude"],
description = ["List of patches to exclude."],
) )
private var excludedPatches = hashSetOf<String>() private var excludedPatches = hashSetOf<String>()
@CommandLine.Option( @CommandLine.Option(
names = ["--ei"], names = ["--ei"],
description = ["List of patches to exclude by their index in relation to the supplied patch bundles."] description = ["List of patches to exclude by their index in relation to the supplied patch bundles."],
) )
private var excludedPatchesByIndex = arrayOf<Int>() private var excludedPatchesByIndex = arrayOf<Int>()
@CommandLine.Option( @CommandLine.Option(
names = ["--options"], description = ["Path to patch options JSON file."], showDefaultValue = ALWAYS names = ["--options"],
description = ["Path to patch options JSON file."],
) )
private var optionsFile: File = File("options.json") private var optionsFile: File? = null
@CommandLine.Option( @CommandLine.Option(
names = ["--exclusive"], names = ["--exclusive"],
description = ["Only include patches that are explicitly specified to be included."], description = ["Only include patches that are explicitly specified to be included."],
showDefaultValue = ALWAYS showDefaultValue = ALWAYS,
) )
private var exclusive = false private var exclusive = false
@CommandLine.Option( @CommandLine.Option(
names = ["-f","--force"], names = ["-f", "--force"],
description = ["Bypass compatibility checks for the supplied APK's version."], description = ["Bypass compatibility checks for the supplied APK's version."],
showDefaultValue = ALWAYS showDefaultValue = ALWAYS,
) )
private var force: Boolean = false private var force: Boolean = false
@CommandLine.Option( private var outputFilePath: File? = null
names = ["-o", "--out"], description = ["Path to save the patched APK file to."], required = true
)
private lateinit var outputFilePath: File
@CommandLine.Option( @CommandLine.Option(
names = ["-d", "--device-serial"], description = ["ADB device serial to install to."], showDefaultValue = ALWAYS names = ["-o", "--out"],
description = ["Path to save the patched APK file to. Defaults to the same directory as the supplied APK file."],
)
private fun setOutputFilePath(outputFilePath: File?) {
this.outputFilePath = outputFilePath?.absoluteFile
}
@CommandLine.Option(
names = ["-d", "--device-serial"],
description = ["ADB device serial to install to. If not supplied, the first connected device will be used."],
fallbackValue = "", // Empty string to indicate that the first connected device should be used.
arity = "0..1",
) )
private var deviceSerial: String? = null private var deviceSerial: String? = null
@CommandLine.Option( @CommandLine.Option(
names = ["--mount"], description = ["Install by mounting the patched APK file."], showDefaultValue = ALWAYS names = ["--mount"],
description = ["Install by mounting the patched APK file."],
showDefaultValue = ALWAYS,
) )
private var mount: Boolean = false private var mount: Boolean = false
@CommandLine.Option( @CommandLine.Option(
names = ["--keystore"], description = ["Path to the keystore to sign the patched APK file with."], names = ["--keystore"],
description = [
"Path to the keystore to sign the patched APK file with. " +
"Defaults to the same directory as the supplied APK file.",
],
) )
private var keystoreFilePath: File? = null private var keystoreFilePath: File? = null
// key store password // key store password
@CommandLine.Option( @CommandLine.Option(
names = ["--keystore-password"], names = ["--keystore-password"],
description = ["The password of the keystore to sign the patched APK file with."], description = ["The password of the keystore to sign the patched APK file with. Empty password by default."],
) )
private var keyStorePassword: String? = null // Empty password by default private var keyStorePassword: String? = null // Empty password by default
@CommandLine.Option( @CommandLine.Option(
names = ["--alias"], description = ["The alias of the key from the keystore to sign the patched APK file with."], names = ["--alias"],
showDefaultValue = ALWAYS description = ["The alias of the key from the keystore to sign the patched APK file with."],
showDefaultValue = ALWAYS,
) )
private var alias = "ReVanced Key" private var alias = "ReVanced Key"
@CommandLine.Option( @CommandLine.Option(
names = ["--keystore-entry-password"], names = ["--keystore-entry-password"],
description = ["The password of the entry from the keystore for the key to sign the patched APK file with."] description = ["The password of the entry from the keystore for the key to sign the patched APK file with."],
) )
private var password = "" // Empty password by default private var password = "" // Empty password by default
@CommandLine.Option( @CommandLine.Option(
names = ["--signer"], description = ["The name of the signer to sign the patched APK file with."], names = ["--signer"],
showDefaultValue = ALWAYS description = ["The name of the signer to sign the patched APK file with."],
showDefaultValue = ALWAYS,
) )
private var signer = "ReVanced" private var signer = "ReVanced"
@CommandLine.Option( @CommandLine.Option(
names = ["-r", "--resource-cache"], names = ["-r", "--resource-cache"],
description = ["Path to temporary resource cache directory."], description = ["Path to temporary resource cache directory."],
showDefaultValue = ALWAYS
) )
private var resourceCachePath = File("revanced-resource-cache.") private var resourceCachePath: File? = null
private var aaptBinaryPath: File? = null private var aaptBinaryPath: File? = null
@CommandLine.Option( @CommandLine.Option(
names = ["-p", "--purge"], names = ["-p", "--purge"],
description = ["Purge the temporary resource cache directory after patching."], description = ["Purge the temporary resource cache directory after patching."],
showDefaultValue = ALWAYS showDefaultValue = ALWAYS,
) )
private var purge: Boolean = false private var purge: Boolean = false
@CommandLine.Option( @CommandLine.Option(
names = ["-w", "--warn"], names = ["-w", "--warn"],
description = ["Warn if a patch can not be found in the supplied patch bundles."], description = ["Warn if a patch can not be found in the supplied patch bundles."],
showDefaultValue = ALWAYS showDefaultValue = ALWAYS,
) )
private var warn: Boolean = false private var warn: Boolean = false
@CommandLine.Parameters( @CommandLine.Parameters(
description = ["APK file to be patched."], arity = "1..1" description = ["APK file to be patched."],
arity = "1..1",
) )
@Suppress("unused") @Suppress("unused")
private fun setApk(apk: File) { private fun setApk(apk: File) {
if (!apk.exists()) throw CommandLine.ParameterException( if (!apk.exists()) {
spec.commandLine(), throw CommandLine.ParameterException(
"APK file ${apk.name} does not exist" spec.commandLine(),
) "APK file ${apk.name} does not exist",
)
}
this.apk = apk this.apk = apk
} }
@CommandLine.Option( @CommandLine.Option(
names = ["-m", "--merge"], description = ["One or more DEX files or containers to merge into the APK."] names = ["-m", "--merge"],
description = ["One or more DEX files or containers to merge into the APK."],
) )
@Suppress("unused") @Suppress("unused")
private fun setIntegrations(integrations: Array<File>) { private fun setIntegrations(integrations: Array<File>) {
@ -167,7 +189,9 @@ internal object PatchCommand : Runnable {
} }
@CommandLine.Option( @CommandLine.Option(
names = ["-b", "--patch-bundle"], description = ["One or more bundles of patches."], required = true names = ["-b", "--patch-bundle"],
description = ["One or more bundles of patches."],
required = true,
) )
@Suppress("unused") @Suppress("unused")
private fun setPatchBundles(patchBundles: Array<File>) { private fun setPatchBundles(patchBundles: Array<File>) {
@ -178,19 +202,43 @@ internal object PatchCommand : Runnable {
} }
@CommandLine.Option( @CommandLine.Option(
names = ["--custom-aapt2-binary"], description = ["Path to a custom AAPT binary to compile resources with."] names = ["--custom-aapt2-binary"],
description = ["Path to a custom AAPT binary to compile resources with."],
) )
@Suppress("unused") @Suppress("unused")
private fun setAaptBinaryPath(aaptBinaryPath: File) { private fun setAaptBinaryPath(aaptBinaryPath: File) {
if (!aaptBinaryPath.exists()) throw CommandLine.ParameterException( if (!aaptBinaryPath.exists()) {
spec.commandLine(), throw CommandLine.ParameterException(
"AAPT binary ${aaptBinaryPath.name} does not exist" spec.commandLine(),
) "AAPT binary ${aaptBinaryPath.name} does not exist",
)
}
this.aaptBinaryPath = aaptBinaryPath this.aaptBinaryPath = aaptBinaryPath
} }
override fun run() { override fun run() {
val adbManager = deviceSerial?.let { serial -> AdbManager.getAdbManager(serial, mount) } // region Setup
val outputFilePath =
outputFilePath ?: File("").absoluteFile.resolve(
"${apk.nameWithoutExtension}-patched.${apk.extension}",
)
val resourceCachePath =
resourceCachePath ?: outputFilePath.parentFile.resolve(
"${outputFilePath.nameWithoutExtension}-resource-cache",
)
val optionsFile =
optionsFile ?: outputFilePath.parentFile.resolve(
"${outputFilePath.nameWithoutExtension}-options.json",
)
val keystoreFilePath =
keystoreFilePath ?: outputFilePath.parentFile
.resolve("${outputFilePath.nameWithoutExtension}.keystore")
// endregion
// region Load patches // region Load patches
@ -199,13 +247,15 @@ internal object PatchCommand : Runnable {
val patches = PatchBundleLoader.Jar(*patchBundles.toTypedArray()) val patches = PatchBundleLoader.Jar(*patchBundles.toTypedArray())
// Warn if a patch can not be found in the supplied patch bundles. // Warn if a patch can not be found in the supplied patch bundles.
if (warn) patches.map { it.name }.toHashSet().let { availableNames -> if (warn) {
(includedPatches + excludedPatches).filter { name -> patches.map { it.name }.toHashSet().let { availableNames ->
!availableNames.contains(name) (includedPatches + excludedPatches).filter { name ->
!availableNames.contains(name)
}
}.let { unknownPatches ->
if (unknownPatches.isEmpty()) return@let
logger.warning("Unknown input of patches:\n${unknownPatches.joinToString("\n")}")
} }
}.let { unknownPatches ->
if (unknownPatches.isEmpty()) return@let
logger.warning("Unknown input of patches:\n${unknownPatches.joinToString("\n")}")
} }
// endregion // endregion
@ -216,64 +266,72 @@ internal object PatchCommand : Runnable {
resourceCachePath, resourceCachePath,
aaptBinaryPath?.path, aaptBinaryPath?.path,
resourceCachePath.absolutePath, resourceCachePath.absolutePath,
true true,
) ),
).use { patcher -> ).use { patcher ->
val filteredPatches = patcher.filterPatchSelection(patches).also { patches -> val filteredPatches =
logger.info("Setting patch options") patcher.filterPatchSelection(patches).also { patches ->
logger.info("Setting patch options")
if (optionsFile.exists()) patches.setOptions(optionsFile) if (optionsFile.exists()) {
else Options.serialize(patches, prettyPrint = true).let(optionsFile::writeText) patches.setOptions(optionsFile)
} } else {
Options.serialize(patches, prettyPrint = true).let(optionsFile::writeText)
}
}
// region Patch // region Patch
val patcherResult = patcher.apply { val patcherResult =
acceptIntegrations(integrations) patcher.apply {
acceptPatches(filteredPatches.toList()) acceptIntegrations(integrations)
acceptPatches(filteredPatches.toList())
// Execute patches. // Execute patches.
runBlocking { runBlocking {
apply(false).collect { patchResult -> apply(false).collect { patchResult ->
patchResult.exception?.let { patchResult.exception?.let {
StringWriter().use { writer -> StringWriter().use { writer ->
it.printStackTrace(PrintWriter(writer)) it.printStackTrace(PrintWriter(writer))
logger.severe("${patchResult.patch.name} failed:\n$writer") logger.severe("${patchResult.patch.name} failed:\n$writer")
} }
} ?: logger.info("${patchResult.patch.name} succeeded") } ?: logger.info("${patchResult.patch.name} succeeded")
}
} }
} }.get()
}.get()
// endregion // endregion
// region Save // region Save
val alignedFile = resourceCachePath.resolve(apk.name).apply { val alignedFile =
ApkUtils.copyAligned(apk, this, patcherResult) resourceCachePath.resolve(apk.name).apply {
} ApkUtils.copyAligned(apk, this, patcherResult)
}
val keystoreFilePath = keystoreFilePath ?: outputFilePath.absoluteFile.parentFile if (!mount) {
.resolve("${outputFilePath.nameWithoutExtension}.keystore") ApkUtils.sign(
alignedFile,
if (!mount) ApkUtils.sign( outputFilePath,
alignedFile, ApkUtils.SigningOptions(
outputFilePath, keystoreFilePath,
ApkUtils.SigningOptions( keyStorePassword,
keystoreFilePath, alias,
keyStorePassword, password,
alias, signer,
password, ),
signer
) )
) } else {
else alignedFile.renameTo(outputFilePath) alignedFile.renameTo(outputFilePath)
}
// endregion // endregion
// region Install // region Install
adbManager?.install(AdbManager.Apk(outputFilePath, patcher.context.packageMetadata.packageName)) deviceSerial?.let { serial ->
AdbManager.getAdbManager(deviceSerial = serial.ifEmpty { null }, mount)
}?.install(AdbManager.Apk(outputFilePath, patcher.context.packageMetadata.packageName))
// endregion // endregion
} }
@ -284,62 +342,70 @@ internal object PatchCommand : Runnable {
} }
} }
/** /**
* Filter the patches to be added to the patcher. The filter is based on the following: * Filter the patches to be added to the patcher. The filter is based on the following:
* *
* @param patches The patches to filter. * @param patches The patches to filter.
* @return The filtered patches. * @return The filtered patches.
*/ */
private fun Patcher.filterPatchSelection(patches: PatchSet): PatchSet = buildSet { private fun Patcher.filterPatchSelection(patches: PatchSet): PatchSet =
val packageName = context.packageMetadata.packageName buildSet {
val packageVersion = context.packageMetadata.packageVersion val packageName = context.packageMetadata.packageName
val packageVersion = context.packageMetadata.packageVersion
patches.withIndex().forEach patch@{ (i, patch) -> patches.withIndex().forEach patch@{ (i, patch) ->
val patchName = patch.name!! val patchName = patch.name!!
val explicitlyExcluded = excludedPatches.contains(patchName) || excludedPatchesByIndex.contains(i) val explicitlyExcluded = excludedPatches.contains(patchName) || excludedPatchesByIndex.contains(i)
if (explicitlyExcluded) return@patch logger.info("Excluding $patchName") if (explicitlyExcluded) return@patch logger.info("Excluding $patchName")
// Make sure the patch is compatible with the supplied APK files package name and version. // Make sure the patch is compatible with the supplied APK files package name and version.
patch.compatiblePackages?.let { packages -> patch.compatiblePackages?.let { packages ->
packages.singleOrNull { it.name == packageName }?.let { `package` -> packages.singleOrNull { it.name == packageName }?.let { `package` ->
val matchesVersion = force || `package`.versions?.let { val matchesVersion =
it.any { version -> version == packageVersion } force || `package`.versions?.let {
} ?: true it.any { version -> version == packageVersion }
} ?: true
if (!matchesVersion) return@patch logger.warning( if (!matchesVersion) {
"$patchName is incompatible with version $packageVersion. " return@patch logger.warning(
+ "This patch is only compatible with version " "$patchName is incompatible with version $packageVersion. " +
+ packages.joinToString(";") { pkg -> "This patch is only compatible with version " +
pkg.versions!!.joinToString(", ") packages.joinToString(";") { pkg ->
pkg.versions!!.joinToString(", ")
},
)
} }
} ?: return@patch logger.fine(
"$patchName is incompatible with $packageName. " +
"This patch is only compatible with " +
packages.joinToString(", ") { `package` -> `package`.name },
) )
} ?: return@patch logger.fine(
"$patchName is incompatible with $packageName. "
+ "This patch is only compatible with "
+ packages.joinToString(", ") { `package` -> `package`.name })
return@let return@let
} ?: logger.fine("$patchName has no constraint on packages.") } ?: logger.fine("$patchName has no constraint on packages.")
// If the patch is implicitly used, it will be only included if [exclusive] is false. // If the patch is implicitly used, it will be only included if [exclusive] is false.
val implicitlyIncluded = !exclusive && patch.use val implicitlyIncluded = !exclusive && patch.use
// If the patch is explicitly used, it will be included even if [exclusive] is false. // If the patch is explicitly used, it will be included even if [exclusive] is false.
val explicitlyIncluded = includedPatches.contains(patchName) || includedPatchesByIndex.contains(i) val explicitlyIncluded = includedPatches.contains(patchName) || includedPatchesByIndex.contains(i)
val included = implicitlyIncluded || explicitlyIncluded val included = implicitlyIncluded || explicitlyIncluded
if (!included) return@patch logger.info("$patchName excluded") // Case 1. if (!included) return@patch logger.info("$patchName excluded") // Case 1.
logger.fine("Adding $patchName") logger.fine("Adding $patchName")
add(patch) add(patch)
}
} }
}
private fun purge(resourceCachePath: File) { private fun purge(resourceCachePath: File) {
val result = if (resourceCachePath.deleteRecursively()) "Purged resource cache directory" val result =
else "Failed to purge resource cache directory" if (resourceCachePath.deleteRecursively()) {
"Purged resource cache directory"
} else {
"Failed to purge resource cache directory"
}
logger.info(result) logger.info(result)
} }
} }

View File

@ -5,20 +5,23 @@ import picocli.CommandLine.*
import java.io.File import java.io.File
import java.util.logging.Logger import java.util.logging.Logger
@Command( @Command(
name = "install", description = ["Install an APK file to devices with the supplied ADB device serials"] name = "install",
description = ["Install an APK file to devices with the supplied ADB device serials"],
) )
internal object InstallCommand : Runnable { internal object InstallCommand : Runnable {
private val logger = Logger.getLogger(InstallCommand::class.java.name) private val logger = Logger.getLogger(InstallCommand::class.java.name)
@Parameters( @Parameters(
description = ["ADB device serials"], arity = "1..*" description = ["ADB device serials. If not supplied, the first connected device will be used."],
arity = "0..*",
) )
private lateinit var deviceSerials: Array<String> private var deviceSerials: Array<String>? = null
@Option( @Option(
names = ["-a", "--apk"], description = ["APK file to be installed"], required = true names = ["-a", "--apk"],
description = ["APK file to be installed"],
required = true,
) )
private lateinit var apk: File private lateinit var apk: File
@ -28,11 +31,14 @@ internal object InstallCommand : Runnable {
) )
private var packageName: String? = null private var packageName: String? = null
override fun run() = deviceSerials.forEach { deviceSerial -> override fun run() {
try { fun install(deviceSerial: String? = null) =
AdbManager.getAdbManager(deviceSerial, packageName != null).install(AdbManager.Apk(apk, packageName)) try {
} catch (e: AdbManager.DeviceNotFoundException) { AdbManager.getAdbManager(deviceSerial, packageName != null).install(AdbManager.Apk(apk, packageName))
logger.severe(e.toString()) } catch (e: AdbManager.DeviceNotFoundException) {
} logger.severe(e.toString())
}
deviceSerials?.forEach(::install) ?: install()
} }
} }

View File

@ -5,32 +5,41 @@ import picocli.CommandLine.*
import picocli.CommandLine.Help.Visibility.ALWAYS import picocli.CommandLine.Help.Visibility.ALWAYS
import java.util.logging.Logger import java.util.logging.Logger
@Command( @Command(
name = "uninstall", name = "uninstall",
description = ["Uninstall a patched app from the devices with the supplied ADB device serials"] description = ["Uninstall a patched app from the devices with the supplied ADB device serials"],
) )
internal object UninstallCommand : Runnable { internal object UninstallCommand : Runnable {
private val logger = Logger.getLogger(UninstallCommand::class.java.name) private val logger = Logger.getLogger(UninstallCommand::class.java.name)
@Parameters(description = ["ADB device serials"], arity = "1..*") @Parameters(
private lateinit var deviceSerials: Array<String> description = ["ADB device serials. If not supplied, the first connected device will be used."],
arity = "0..*",
)
private var deviceSerials: Array<String>? = null
@Option(names = ["-p", "--package-name"], description = ["Package name of the app to uninstall"], required = true) @Option(
names = ["-p", "--package-name"],
description = ["Package name of the app to uninstall"],
required = true,
)
private lateinit var packageName: String private lateinit var packageName: String
@Option( @Option(
names = ["-u", "--unmount"], names = ["-u", "--unmount"],
description = ["Uninstall by unmounting the patched APK file"], description = ["Uninstall by unmounting the patched APK file"],
showDefaultValue = ALWAYS showDefaultValue = ALWAYS,
) )
private var unmount: Boolean = false private var unmount: Boolean = false
override fun run() = deviceSerials.forEach { deviceSerial -> override fun run() {
try { fun uninstall(deviceSerial: String? = null) =
AdbManager.getAdbManager(deviceSerial, unmount).uninstall(packageName) try {
} catch (e: AdbManager.DeviceNotFoundException) { AdbManager.getAdbManager(deviceSerial, unmount).uninstall(packageName)
logger.severe(e.toString()) } catch (e: AdbManager.DeviceNotFoundException) {
} logger.severe(e.toString())
}
deviceSerials?.forEach { uninstall(it) } ?: uninstall()
} }
} }

View File

@ -7,4 +7,4 @@ import picocli.CommandLine
description = ["Commands for utility purposes"], description = ["Commands for utility purposes"],
subcommands = [InstallCommand::class, UninstallCommand::class], subcommands = [InstallCommand::class, UninstallCommand::class],
) )
internal object UtilityCommand internal object UtilityCommand