Compare commits

..

30 Commits

Author SHA1 Message Date
Yang a421e43855 Merge branch 'main' into release/v2
* main:
  Prepare for release 2.38.0.
  Revert "Add `minorApiLevel` for compile SDK.  Test with API 37.0."
  Add `minorApiLevel` for compile SDK.  Test with API 37.0.
  Build tools 37.0.0 (#475)
  Add FreeOrion to the list (#470)
2026-07-05 15:05:11 +10:00
Yang cad321644d Prepare for release 2.38.0. 2026-07-05 15:04:14 +10:00
Yang ed6254cde0 Revert "Add minorApiLevel for compile SDK. Test with API 37.0."
This reverts commit 8acdc1106b.
2026-06-16 21:33:52 +10:00
Yang 8acdc1106b Add minorApiLevel for compile SDK. Test with API 37.0. 2026-06-16 21:30:19 +10:00
TÖRÖK Attila a917366169 Build tools 37.0.0 (#475) 2026-06-16 10:56:06 +10:00
O01eg ae2c4d7c48 Add FreeOrion to the list (#470) 2026-05-05 09:03:31 +10:00
Yang e89f39f1ab Merge branch 'main' into release/v2
* main:
  Prepare for release 2.37.0.
  Build build tools 36.0.0, SDK command-line tools 12.0. (#467)
  Update to Node 24. (#466)
  Modernize test-fixture project. (#465)
2026-03-14 19:50:13 +11:00
Yang 9644b1a1f9 Prepare for release 2.37.0. 2026-03-14 19:49:27 +11:00
Yang 4fe4b1ae37 Build build tools 36.0.0, SDK command-line tools 12.0. (#467) 2026-03-14 19:47:22 +11:00
Yang 6fe36e1748 Update to Node 24. (#466) 2026-03-14 19:10:07 +11:00
Yang 597aa7c21b Modernize test-fixture project. (#465) 2026-03-14 17:27:13 +11:00
Yang 5d6e86df22 Merge branch 'main' into release/v2
* main:
  Prepare for release 2.36.0.
  Add F3D in the list of projects (#462)
  fix: remove problematic / unneeded `--abi` option in avd creation (#456)
  Replace deprecated `macos-13` runner with `macos-15-intel`. (#460)
  Add Compose Unstyled to the list of users (#457)
2026-03-11 23:16:23 +11:00
Yang 38164a29c1 Prepare for release 2.36.0. 2026-03-11 23:15:24 +11:00
Michael MIGLIORE 969c9896d3 Add F3D in the list of projects (#462) 2026-02-26 22:44:03 +11:00
Mike Hardy 29b35ed135 fix: remove problematic / unneeded --abi option in avd creation (#456) 2026-02-06 17:13:54 +11:00
Yang 001613e856 Replace deprecated macos-13 runner with macos-15-intel. (#460) 2026-02-06 17:13:30 +11:00
Alex Styl 937ab7eebc Add Compose Unstyled to the list of users (#457) 2025-11-28 23:18:06 +11:00
Yang b530d96654 Merge branch 'main' into release/v2
* main:
  Prepare for release 2.35.0.
  docs: update AVD profile description (#452)
  README: Fix imbalanced backtick in `Configurations` table (#445)
  fix: allow google_apis_ps16k as a valid target (#440)
  Fix `pre-emulator-launch-script` (#439)
  Optimize config.ini updates and efficiency improvements report (#436)
  Fix outdated information about larger runners billing (#437)
2025-11-07 12:33:32 +11:00
Yang 016d4d097a Prepare for release 2.35.0. 2025-11-07 12:32:15 +11:00
Vojtech Novak f9bdb6d84e docs: update AVD profile description (#452) 2025-09-11 23:07:58 +10:00
Marijn Suijten b68ca169d6 README: Fix imbalanced backtick in Configurations table (#445) 2025-07-09 20:52:11 +10:00
Mike Hardy 66283c0319 fix: allow google_apis_ps16k as a valid target (#440)
* test: remove target validator test

these appear to have too much maintenance burden as they only ever
add more over time

* fix: allow google_apis_ps16k and google_apis_playstore_ps16k as valid targets

Fixes #403

Co-authored-by: Yang <reactivecircus@gmail.com>

* fix: remove target validation entirely, any valid sdkmanager target will work

no longer requires code changes here to access new targets

---------

Co-authored-by: Yang <reactivecircus@gmail.com>
2025-07-02 11:45:42 +10:00
munrocket f2bf410054 Fix pre-emulator-launch-script (#439) 2025-06-26 00:48:55 +10:00
devin-ai-integration[bot] 62e6348453 Optimize config.ini updates and efficiency improvements report (#436)
* Optimize config.ini updates by batching shell executions

- Reduce up to 5 separate shell executions to 1 for AVD configuration
- Improves performance by eliminating redundant process spawns
- Add comprehensive efficiency report documenting all identified improvements
- All existing tests pass, no functional changes

Co-Authored-By: Yang <reactivecircus@gmail.com>

* Add compiled JS files and remove standalone efficiency report

- Include built lib/emulator-manager.js with batched config optimization
- Remove EFFICIENCY_REPORT.md as requested
- Efficiency report content will be moved to PR description

Co-Authored-By: Yang <reactivecircus@gmail.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Yang <reactivecircus@gmail.com>
2025-06-15 03:00:24 +10:00
devin-ai-integration[bot] 450c4c9f73 Fix outdated information about larger runners billing (#437)
Remove incorrect claim that larger runners are free for public repos.
Current GitHub documentation states they are not free.

Fixes #435

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Yang <reactivecircus@gmail.com>
2025-06-15 02:59:42 +10:00
Yang 1dcd009011 Merge branch 'main' into release/v2
* main:
  Prepare for release 2.34.0.
  Update README.md and CHANGELOG.md.
  Support API levels for SDK extensions and add missing targets for automotive and desktop. (#428)
  Add `Baklava` support (#424)
2025-03-29 01:51:10 +11:00
Yang 2548f9f3fc Prepare for release 2.34.0. 2025-03-29 01:48:44 +11:00
Yang 595a4563df Update README.md and CHANGELOG.md. 2025-03-29 01:33:05 +11:00
Timothy a173d65200 Support API levels for SDK extensions and add missing targets for automotive and desktop. (#428) 2025-03-29 01:04:02 +11:00
LoveSy 50d5b10c01 Add Baklava support (#424)
* Add `Baklava` support

* Fix typo of `ubuntu-latest` to fix CI
2025-01-24 22:46:33 +11:00
33 changed files with 4697 additions and 3850 deletions
+2 -2
View File
@@ -9,5 +9,5 @@ jobs:
validate-typings: validate-typings:
runs-on: "ubuntu-latest" runs-on: "ubuntu-latest"
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
- uses: krzema12/github-actions-typing@v0 - uses: krzema12/github-actions-typing@v2
+14 -7
View File
@@ -27,7 +27,7 @@ jobs:
api-level: 24 api-level: 24
target: playstore target: playstore
arch: x86 arch: x86
- os: macos-13 - os: macos-15-intel
api-level: 31 api-level: 31
target: default target: default
arch: x86_64 arch: x86_64
@@ -35,14 +35,19 @@ jobs:
api-level: 34 api-level: 34
target: aosp_atd target: aosp_atd
arch: x86_64 arch: x86_64
- os: ubuntu-lastest - os: ubuntu-latest
api-level: 35 api-level: 35
target: google_apis target: google_apis
arch: x86_64 arch: x86_64
- os: ubuntu-latest
api-level: 34-ext10
target: android-automotive
arch: x86_64
system-image-api-level: 34-ext9
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: build, test and lint - name: build, test and lint
run: | run: |
@@ -51,12 +56,12 @@ jobs:
npm run lint npm run lint
npm test npm test
- uses: actions/setup-java@v4 - uses: actions/setup-java@v5
with: with:
distribution: 'zulu' distribution: 'zulu'
java-version: 23 java-version: 25
- uses: actions/cache@v4 - uses: actions/cache@v5
id: avd-cache id: avd-cache
with: with:
path: | path: |
@@ -65,7 +70,7 @@ jobs:
~/.android/debug.keystore ~/.android/debug.keystore
key: avd-${{ matrix.api-level }}-${{ matrix.os }}-${{ matrix.target }}-${{ matrix.arch }} key: avd-${{ matrix.api-level }}-${{ matrix.os }}-${{ matrix.target }}-${{ matrix.arch }}
- uses: gradle/actions/setup-gradle@v4 - uses: gradle/actions/setup-gradle@v5
- name: assemble tests - name: assemble tests
working-directory: test-fixture working-directory: test-fixture
@@ -85,6 +90,7 @@ jobs:
api-level: ${{ matrix.api-level }} api-level: ${{ matrix.api-level }}
target: ${{ matrix.target }} target: ${{ matrix.target }}
arch: ${{ matrix.arch }} arch: ${{ matrix.arch }}
system-image-api-level: ${{ matrix.system-image-api-level }}
profile: Galaxy Nexus profile: Galaxy Nexus
cores: 2 cores: 2
sdcard-path-or-size: 100M sdcard-path-or-size: 100M
@@ -102,6 +108,7 @@ jobs:
api-level: ${{ matrix.api-level }} api-level: ${{ matrix.api-level }}
target: ${{ matrix.target }} target: ${{ matrix.target }}
arch: ${{ matrix.arch }} arch: ${{ matrix.arch }}
system-image-api-level: ${{ matrix.system-image-api-level }}
profile: Galaxy Nexus profile: Galaxy Nexus
cores: 2 cores: 2
ram-size: 2048M ram-size: 2048M
+9 -6
View File
@@ -7,11 +7,13 @@ on:
required: true required: true
default: 'ubuntu-latest' default: 'ubuntu-latest'
api-level: api-level:
description: 'API level of the platform and system image' description: 'API level of the platform and system image (if not overridden with system-image-api-level input) - e.g. 33, 35-ext15, Baklava'
required: true required: true
default: '34' default: '34'
system-image-api-level:
description: 'API level of the system image - e.g. 34-ext10, 35-ext15'
target: target:
description: 'target of the system image - default, google_apis, google_apis_playstore, aosp_atd, google_atd, android-wear, android-wear-cn, android-tv or google-tv' description: 'target of the system image - default, google_apis, google_apis_playstore, aosp_atd, google_atd, android-wear, android-wear-cn, android-tv, google-tv, android-automotive, android-automotive-playstore or android-desktop'
required: true required: true
default: 'default' default: 'default'
arch: arch:
@@ -39,7 +41,7 @@ jobs:
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: build, test and lint - name: build, test and lint
run: | run: |
@@ -48,12 +50,12 @@ jobs:
npm run lint npm run lint
npm test npm test
- uses: actions/setup-java@v4 - uses: actions/setup-java@v5
with: with:
distribution: 'zulu' distribution: 'zulu'
java-version: 23 java-version: 25
- uses: gradle/actions/setup-gradle@v4 - uses: gradle/actions/setup-gradle@v5
- name: enable KVM for linux runners - name: enable KVM for linux runners
if: runner.os == 'Linux' if: runner.os == 'Linux'
@@ -68,6 +70,7 @@ jobs:
api-level: ${{ github.event.inputs.api-level }} api-level: ${{ github.event.inputs.api-level }}
target: ${{ github.event.inputs.target }} target: ${{ github.event.inputs.target }}
arch: ${{ github.event.inputs.arch }} arch: ${{ github.event.inputs.arch }}
system-image-api-level: ${{ github.event.inputs.system-image-api-level }}
profile: Galaxy Nexus profile: Galaxy Nexus
emulator-options: ${{ github.event.inputs.emulator-options }} emulator-options: ${{ github.event.inputs.emulator-options }}
emulator-build: ${{ github.event.inputs.emulator-build }} emulator-build: ${{ github.event.inputs.emulator-build }}
+1
View File
@@ -0,0 +1 @@
24
+29
View File
@@ -1,5 +1,34 @@
# Change Log # Change Log
## Unreleased
## v2.38.0
* Update SDK build tools to `37.0.0`. - [#475](https://github.com/ReactiveCircus/android-emulator-runner/pull/475)
## v2.37.0
* Update SDK build tools to `36.0.0`, update SDK command-line tools to `20.0`. - [#467](https://github.com/ReactiveCircus/android-emulator-runner/pull/467)
* Run action on Node 24. - [#466](https://github.com/ReactiveCircus/android-emulator-runner/pull/466)
* Update Node and npm packages. - [#466](https://github.com/ReactiveCircus/android-emulator-runner/pull/466)
## v2.36.0
* Remove `--abi` option to better support 16-KB-based system images (#456).
## v2.35.0
* Optimize config.ini updates and efficiency improvements report (#436).
* Fix `pre-emulator-launch-script` (#439).
* Allow `google_apis_ps16k` as a valid target (#440).
## v2.34.0
* Allow arbitrary value for `api-level` such as `Baklava` and `35-ext15`. - [428](https://github.com/ReactiveCircus/android-emulator-runner/pull/428)
* Add option to specify `system-image-api-level` to use a system image with a different API level than the platform. - [428](https://github.com/ReactiveCircus/android-emulator-runner/pull/428)
* Support automotive and desktop targets: `android-automotive`, `android-automotive-playstore`, `android-desktop`. - [428](https://github.com/ReactiveCircus/android-emulator-runner/pull/428)
## v2.33.0 ## v2.33.0
* Fix missing AVD directory in `ubuntu-24.04` runner. - [415](https://github.com/ReactiveCircus/android-emulator-runner/pull/415) * Fix missing AVD directory in `ubuntu-24.04` runner. - [415](https://github.com/ReactiveCircus/android-emulator-runner/pull/415)
+43 -13
View File
@@ -12,7 +12,7 @@ This presents a challenge when running emulators on CI especially when running e
## Running hardware accelerated emulators on Linux runners ## Running hardware accelerated emulators on Linux runners
GitHub's [larger Linux runners support running hardware accelerated emulators](https://github.blog/changelog/2023-02-23-hardware-accelerated-android-virtualization-on-actions-windows-and-linux-larger-hosted-runners/) which is [free for public GitHub repos](https://github.blog/2024-01-17-github-hosted-runners-double-the-power-for-open-source/). It is now recommended to use the **Ubuntu** (`ubuntu-latest`) runners which are 2-3 times faster than the **macOS** ones which are also a lot more expensive. Remember to enable KVM in your workflow before running this action: GitHub's [larger Linux runners support running hardware accelerated emulators](https://github.blog/changelog/2023-02-23-hardware-accelerated-android-virtualization-on-actions-windows-and-linux-larger-hosted-runners/). It is now recommended to use the **Ubuntu** (`ubuntu-latest`) runners which are 2-3 times faster than the **macOS** ones which are also a lot more expensive. Remember to enable KVM in your workflow before running this action:
``` ```
- name: Enable KVM group perms - name: Enable KVM group perms
@@ -49,7 +49,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: Enable KVM - name: Enable KVM
run: | run: |
@@ -76,7 +76,7 @@ jobs:
target: [default, google_apis] target: [default, google_apis]
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: Enable KVM - name: Enable KVM
run: | run: |
@@ -90,7 +90,7 @@ jobs:
api-level: ${{ matrix.api-level }} api-level: ${{ matrix.api-level }}
target: ${{ matrix.target }} target: ${{ matrix.target }}
arch: x86_64 arch: x86_64
profile: Nexus 6 profile: pixel_7_pro
script: ./gradlew connectedCheck script: ./gradlew connectedCheck
``` ```
@@ -102,7 +102,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: Enable KVM - name: Enable KVM
run: | run: |
@@ -119,10 +119,35 @@ jobs:
script: ./gradlew connectedCheck script: ./gradlew connectedCheck
``` ```
If you need a specific [SDK Extensions](https://developer.android.com/guide/sdk-extensions) for the system image but not the platform:
```yml
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v6
- name: Enable KVM
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- name: run tests
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 34
system-image-api-level: 34-ext9
target: android-automotive
script: ./gradlew connectedCheck
```
We can significantly reduce emulator startup time by setting up AVD snapshot caching: We can significantly reduce emulator startup time by setting up AVD snapshot caching:
1. add a `gradle/actions/setup-gradle@v4` step for caching Gradle, more details see [#229](https://github.com/ReactiveCircus/android-emulator-runner/issues/229) 1. add a `gradle/actions/setup-gradle@v5` step for caching Gradle, more details see [#229](https://github.com/ReactiveCircus/android-emulator-runner/issues/229)
2. add an `actions/cache@v4` step for caching the `avd` 2. add an `actions/cache@v5` step for caching the `avd`
3. add a `reactivecircus/android-emulator-runner@v2` step to generate a clean snapshot - specify `emulator-options` without `no-snapshot` 3. add a `reactivecircus/android-emulator-runner@v2` step to generate a clean snapshot - specify `emulator-options` without `no-snapshot`
4. add another `reactivecircus/android-emulator-runner@v2` step to run your tests using existing AVD / snapshot - specify `emulator-options` with `no-snapshot-save` 4. add another `reactivecircus/android-emulator-runner@v2` step to run your tests using existing AVD / snapshot - specify `emulator-options` with `no-snapshot-save`
@@ -135,7 +160,7 @@ jobs:
api-level: [21, 23, 29] api-level: [21, 23, 29]
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: Enable KVM - name: Enable KVM
run: | run: |
@@ -144,10 +169,10 @@ jobs:
sudo udevadm trigger --name-match=kvm sudo udevadm trigger --name-match=kvm
- name: Gradle cache - name: Gradle cache
uses: gradle/actions/setup-gradle@v3 uses: gradle/actions/setup-gradle@v5
- name: AVD cache - name: AVD cache
uses: actions/cache@v4 uses: actions/cache@v5
id: avd-cache id: avd-cache
with: with:
path: | path: |
@@ -179,10 +204,11 @@ jobs:
| **Input** | **Required** | **Default** | **Description** | | **Input** | **Required** | **Default** | **Description** |
|-|-|-|-| |-|-|-|-|
| `api-level` | Required | N/A | API level of the platform system image - e.g. 23 for Android Marshmallow, 29 for Android 10. **Minimum API level supported is 15**. | | `api-level` | Required | N/A | API level of the platform and system image - e.g. `23`, `33`, `35-ext15`, `Baklava`. **Minimum API level supported is 15**. |
| `target` | Optional | `default` | Target of the system image - `default`, `google_apis`, `playstore`, `android-wear`, `android-wear-cn`, `android-tv`, `google-tv`, `aosp_atd` or `google_atd`. Note that `aosp_atd` and `google_atd` currently require the following: `api-level: 30`, `arch: x86` or `arch: arm64-v8` and `channel: canary`. | | `system-image-api-level` | Optional | same as `api-level` | API level of the system image - e.g. `34-ext10`, `35-ext15`. |
| `target` | Optional | `default` | Target of the system image - e.g. `default`, `google_apis`, `google_apis_ps16k`, `google_apis_playstore`, `google_apis_playstore_ps16k`, `android-wear`, `android-wear-cn`, `android-tv`, `google-tv`, `aosp_atd`, `google_atd`, `android-automotive`, `android-automotive-playstore`, `android-desktop`. Please run `sdkmanager --list` to see the available targets. |
| `arch` | Optional | `x86` | CPU architecture of the system image - `x86`, `x86_64` or `arm64-v8a`. Note that `x86_64` image is only available for API 21+. `arm64-v8a` images require Android 4.2+ and are limited to fewer API levels (e.g. 30). | | `arch` | Optional | `x86` | CPU architecture of the system image - `x86`, `x86_64` or `arm64-v8a`. Note that `x86_64` image is only available for API 21+. `arm64-v8a` images require Android 4.2+ and are limited to fewer API levels (e.g. 30). |
| `profile` | Optional | N/A | Hardware profile used for creating the AVD - e.g. `Nexus 6`. For a list of all profiles available, run `avdmanager list device`. | | `profile` | Optional | N/A | Hardware profile id used for creating the AVD - e.g. `pixel_7_pro`. For a list of all profiles available, run `avdmanager list device`. |
| `cores` | Optional | 2 | Number of cores to use for the emulator (`hw.cpu.ncore` in config.ini). | | `cores` | Optional | 2 | Number of cores to use for the emulator (`hw.cpu.ncore` in config.ini). |
| `ram-size` | Optional | N/A | Size of RAM to use for this AVD, in KB or MB, denoted with K or M. - e.g. `2048M` | | `ram-size` | Optional | N/A | Size of RAM to use for this AVD, in KB or MB, denoted with K or M. - e.g. `2048M` |
| `heap-size` | Optional | N/A | Heap size to use for this AVD, in KB or MB, denoted with K or M. - e.g. `512M` | | `heap-size` | Optional | N/A | Heap size to use for this AVD, in KB or MB, denoted with K or M. - e.g. `512M` |
@@ -243,5 +269,9 @@ These are some of the open-source projects using (or used) **Android Emulator Ru
- [ACRA/acra](https://github.com/ACRA/acra/blob/master/.github/workflows/test.yml) - [ACRA/acra](https://github.com/ACRA/acra/blob/master/.github/workflows/test.yml)
- [bitfireAT/davx5-ose](https://github.com/bitfireAT/davx5-ose/blob/dev-ose/.github/workflows/test-dev.yml) - [bitfireAT/davx5-ose](https://github.com/bitfireAT/davx5-ose/blob/dev-ose/.github/workflows/test-dev.yml)
- [robolectric/robolectric](https://github.com/robolectric/robolectric/blob/master/.github/workflows/tests.yml) - [robolectric/robolectric](https://github.com/robolectric/robolectric/blob/master/.github/workflows/tests.yml)
- [home-assistant/android](https://github.com/home-assistant/android/blob/master/.github/workflows/pr.yml)
- [composablehorizons/compose-unstyled](https://github.com/composablehorizons/compose-unstyled/tree/main/.github/workflows)
- [f3d-app/f3d-android](https://github.com/f3d-app/f3d-android/blob/master/.github/workflows/ci.yml)
- [freeorion/freeorion](https://github.com/freeorion/freeorion/blob/master/.github/workflows/_build-android.yml)
If you are using **Android Emulator Runner** and want your project included in the list, please feel free to open a pull request. If you are using **Android Emulator Runner** and want your project included in the list, please feel free to open a pull request.
-99
View File
@@ -1,105 +1,6 @@
import * as validator from '../src/input-validator'; import * as validator from '../src/input-validator';
import { MAX_PORT, MIN_PORT } from '../src/input-validator'; import { MAX_PORT, MIN_PORT } from '../src/input-validator';
describe('api-level validator tests', () => {
it('Throws if api-level is not a number', () => {
const func = () => {
validator.checkApiLevel('api');
};
expect(func).toThrowError(`Unexpected API level: 'api'.`);
});
it('Throws if api-level is not an integer', () => {
const func = () => {
validator.checkApiLevel('29.1');
};
expect(func).toThrowError(`Unexpected API level: '29.1'.`);
});
it('Throws if api-level is lower than min API supported', () => {
const func = () => {
validator.checkApiLevel('14');
};
expect(func).toThrowError(`Minimum API level supported is ${validator.MIN_API_LEVEL}.`);
});
it('Validates successfully with valid api-level', () => {
const func1 = () => {
validator.checkApiLevel('15');
};
expect(func1).not.toThrow();
const func2 = () => {
validator.checkApiLevel('29');
};
expect(func2).not.toThrow();
const func3 = () => {
validator.checkApiLevel('UpsideDownCake-ext5');
};
expect(func3).not.toThrow();
const func4 = () => {
validator.checkApiLevel('TiramisuPrivacySandbox');
};
expect(func4).not.toThrow();
});
});
describe('target validator tests', () => {
it('Throws if target is unknown', () => {
const func = () => {
validator.checkTarget('some-target');
};
expect(func).toThrowError(`Value for input.target 'some-target' is unknown. Supported options: ${validator.VALID_TARGETS}`);
});
it('Validates successfully with valid target', () => {
const func1 = () => {
validator.checkTarget('default');
};
expect(func1).not.toThrow();
const func2 = () => {
validator.checkTarget('google_apis');
};
expect(func2).not.toThrow();
const func3 = () => {
validator.checkTarget('aosp_atd');
};
expect(func3).not.toThrow();
const func4 = () => {
validator.checkTarget('google_atd');
};
expect(func4).not.toThrow();
const func5 = () => {
validator.checkTarget('google_apis_playstore');
};
expect(func5).not.toThrow();
const func6 = () => {
validator.checkTarget('android-wear');
};
expect(func6).not.toThrow();
const func7 = () => {
validator.checkTarget('android-wear-cn');
};
expect(func7).not.toThrow();
const func8 = () => {
validator.checkTarget('android-tv');
};
expect(func8).not.toThrow();
const func9 = () => {
validator.checkTarget('google-tv');
};
expect(func9).not.toThrow();
});
});
describe('arch validator tests', () => { describe('arch validator tests', () => {
it('Throws if arch is unknown', () => { it('Throws if arch is unknown', () => {
const func = () => { const func = () => {
+6 -1
View File
@@ -1,6 +1,8 @@
inputs: inputs:
api-level: api-level:
type: integer type: string
system-image-api-level:
type: string
target: target:
type: enum type: enum
allowed-values: allowed-values:
@@ -13,6 +15,9 @@ inputs:
- android-wear-cn - android-wear-cn
- android-tv - android-tv
- google-tv - google-tv
- android-automotive
- android-automotive-playstore
- android-desktop
arch: arch:
type: enum type: enum
allowed-values: allowed-values:
+6 -3
View File
@@ -6,10 +6,13 @@ branding:
color: 'green' color: 'green'
inputs: inputs:
api-level: api-level:
description: 'API level of the platform and system image - e.g. 23 for Android Marshmallow, 29 for Android 10' description: 'API level of the platform and system image - e.g. 23, 33, 35-ext15, Baklava'
required: true required: true
system-image-api-level:
description: 'API level of the system image - e.g. 34-ext10, 35-ext15. If not set the `api-level` input will be used.'
required: false
target: target:
description: 'target of the system image - default, google_apis, google_apis_playstore, aosp_atd, google_atd, android-wear, android-wear-cn, android-tv or google-tv' description: 'target of the system image - e.g. default, google_apis, google_apis_ps16k, google_apis_playstore, google_apis_playstore_16k, aosp_atd, google_atd, android-wear, android-wear-cn, android-tv, google-tv, android-automotive, android-automotive-playstore or android-desktop'
default: 'default' default: 'default'
arch: arch:
description: 'CPU architecture of the system image - x86, x86_64 or arm64-v8a' description: 'CPU architecture of the system image - x86, x86_64 or arm64-v8a'
@@ -71,5 +74,5 @@ inputs:
pre-emulator-launch-script: pre-emulator-launch-script:
description: 'custom script to run after creating the AVD and before launching the emulator - e.g. `./adjust-emulator-configs.sh`' description: 'custom script to run after creating the AVD and before launching the emulator - e.g. `./adjust-emulator-configs.sh`'
runs: runs:
using: 'node20' using: 'node24'
main: 'lib/main.js' main: 'lib/main.js'
+1 -2
View File
@@ -1,6 +1,6 @@
"use strict"; "use strict";
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.getChannelId = void 0; exports.getChannelId = getChannelId;
function getChannelId(channelName) { function getChannelId(channelName) {
if (channelName === 'stable') { if (channelName === 'stable') {
return 0; return 0;
@@ -18,4 +18,3 @@ function getChannelId(channelName) {
throw new Error(`Unexpected channel name: '${channelName}'.`); throw new Error(`Unexpected channel name: '${channelName}'.`);
} }
} }
exports.getChannelId = getChannelId;
+130 -119
View File
@@ -15,154 +15,165 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
}) : function(o, v) { }) : function(o, v) {
o["default"] = v; o["default"] = v;
}); });
var __importStar = (this && this.__importStar) || function (mod) { var __importStar = (this && this.__importStar) || (function () {
if (mod && mod.__esModule) return mod; var ownKeys = function(o) {
var result = {}; ownKeys = Object.getOwnPropertyNames || function (o) {
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); var ar = [];
__setModuleDefault(result, mod); for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return result; return ar;
}; };
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return ownKeys(o);
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } };
return new (P || (P = Promise))(function (resolve, reject) { return function (mod) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } if (mod && mod.__esModule) return mod;
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } var result = {};
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
step((generator = generator.apply(thisArg, _arguments || [])).next()); __setModuleDefault(result, mod);
}); return result;
}; };
})();
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.killEmulator = exports.launchEmulator = void 0; exports.createAvd = createAvd;
exports.launchEmulator = launchEmulator;
exports.killEmulator = killEmulator;
const exec = __importStar(require("@actions/exec")); const exec = __importStar(require("@actions/exec"));
const fs = __importStar(require("fs")); const fs = __importStar(require("fs"));
/** /**
* Creates and launches a new AVD instance with the specified configurations. * Creates a new AVD instance with the specified configurations.
*/ */
function launchEmulator(apiLevel, target, arch, profile, cores, ramSize, heapSize, sdcardPathOrSize, diskSize, avdName, forceAvdCreation, emulatorBootTimeout, port, emulatorOptions, disableAnimations, disableSpellChecker, disableLinuxHardwareAcceleration, enableHardwareKeyboard) { async function createAvd(arch, avdName, cores, diskSize, enableHardwareKeyboard, forceAvdCreation, heapSize, profile, ramSize, sdcardPathOrSize, systemImageApiLevel, target) {
return __awaiter(this, void 0, void 0, function* () { try {
try { console.log(`::group::Create AVD`);
console.log(`::group::Launch Emulator`); // create a new AVD if AVD directory does not already exist or forceAvdCreation is true
// create a new AVD if AVD directory does not already exist or forceAvdCreation is true const avdPath = `${process.env.ANDROID_AVD_HOME}/${avdName}.avd`;
const avdPath = `${process.env.ANDROID_AVD_HOME}/${avdName}.avd`; if (!fs.existsSync(avdPath) || forceAvdCreation) {
if (!fs.existsSync(avdPath) || forceAvdCreation) { const profileOption = profile.trim() !== '' ? `--device '${profile}'` : '';
const profileOption = profile.trim() !== '' ? `--device '${profile}'` : ''; const sdcardPathOrSizeOption = sdcardPathOrSize.trim() !== '' ? `--sdcard '${sdcardPathOrSize}'` : '';
const sdcardPathOrSizeOption = sdcardPathOrSize.trim() !== '' ? `--sdcard '${sdcardPathOrSize}'` : ''; console.log(`Creating AVD.`);
console.log(`Creating AVD.`); await exec.exec(`sh -c \\"echo no | avdmanager create avd --force -n "${avdName}" --package 'system-images;android-${systemImageApiLevel};${target};${arch}' ${profileOption} ${sdcardPathOrSizeOption}"`);
yield exec.exec(`sh -c \\"echo no | avdmanager create avd --force -n "${avdName}" --abi '${target}/${arch}' --package 'system-images;android-${apiLevel};${target};${arch}' ${profileOption} ${sdcardPathOrSizeOption}"`); }
} if (cores || ramSize || heapSize || enableHardwareKeyboard || diskSize) {
const configEntries = [];
if (cores) { if (cores) {
yield exec.exec(`sh -c \\"printf 'hw.cpu.ncore=${cores}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`); configEntries.push(`hw.cpu.ncore=${cores}`);
} }
if (ramSize) { if (ramSize) {
yield exec.exec(`sh -c \\"printf 'hw.ramSize=${ramSize}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`); configEntries.push(`hw.ramSize=${ramSize}`);
} }
if (heapSize) { if (heapSize) {
yield exec.exec(`sh -c \\"printf 'hw.heapSize=${heapSize}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`); configEntries.push(`hw.heapSize=${heapSize}`);
} }
if (enableHardwareKeyboard) { if (enableHardwareKeyboard) {
yield exec.exec(`sh -c \\"printf 'hw.keyboard=yes\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`); configEntries.push('hw.keyboard=yes');
} }
if (diskSize) { if (diskSize) {
yield exec.exec(`sh -c \\"printf 'disk.dataPartition.size=${diskSize}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`); configEntries.push(`disk.dataPartition.size=${diskSize}`);
} }
// turn off hardware acceleration on Linux if (configEntries.length > 0) {
if (process.platform === 'linux' && disableLinuxHardwareAcceleration) { const configContent = configEntries.join('\\n') + '\\n';
console.log('Disabling Linux hardware acceleration.'); await exec.exec(`sh -c \\"printf '${configContent}' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini"`);
emulatorOptions += ' -accel off';
}
// start emulator
console.log('Starting emulator.');
yield exec.exec(`sh -c \\"${process.env.ANDROID_HOME}/emulator/emulator -port ${port} -avd "${avdName}" ${emulatorOptions} &"`, [], {
listeners: {
stderr: (data) => {
if (data.toString().includes('invalid command-line parameter')) {
throw new Error(data.toString());
}
},
},
});
// wait for emulator to complete booting
yield waitForDevice(port, emulatorBootTimeout);
yield adb(port, `shell input keyevent 82`);
if (disableAnimations) {
console.log('Disabling animations.');
yield adb(port, `shell settings put global window_animation_scale 0.0`);
yield adb(port, `shell settings put global transition_animation_scale 0.0`);
yield adb(port, `shell settings put global animator_duration_scale 0.0`);
}
if (disableSpellChecker) {
yield adb(port, `shell settings put secure spell_checker_enabled 0`);
}
if (enableHardwareKeyboard) {
yield adb(port, `shell settings put secure show_ime_with_hard_keyboard 0`);
} }
} }
finally { }
console.log(`::endgroup::`); finally {
} console.log(`::endgroup::`);
}); }
}
/**
* Launches an existing AVD instance with the specified configurations.
*/
async function launchEmulator(avdName, disableAnimations, disableLinuxHardwareAcceleration, disableSpellChecker, emulatorBootTimeout, emulatorOptions, enableHardwareKeyboard, port) {
try {
console.log(`::group::Launch Emulator`);
// turn off hardware acceleration on Linux
if (process.platform === 'linux' && disableLinuxHardwareAcceleration) {
console.log('Disabling Linux hardware acceleration.');
emulatorOptions += ' -accel off';
}
// start emulator
console.log('Starting emulator.');
await exec.exec(`sh -c \\"${process.env.ANDROID_HOME}/emulator/emulator -port ${port} -avd "${avdName}" ${emulatorOptions} &"`, [], {
listeners: {
stderr: (data) => {
if (data.toString().includes('invalid command-line parameter')) {
throw new Error(data.toString());
}
},
},
});
// wait for emulator to complete booting
await waitForDevice(port, emulatorBootTimeout);
await adb(port, `shell input keyevent 82`);
if (disableAnimations) {
console.log('Disabling animations.');
await adb(port, `shell settings put global window_animation_scale 0.0`);
await adb(port, `shell settings put global transition_animation_scale 0.0`);
await adb(port, `shell settings put global animator_duration_scale 0.0`);
}
if (disableSpellChecker) {
await adb(port, `shell settings put secure spell_checker_enabled 0`);
}
if (enableHardwareKeyboard) {
await adb(port, `shell settings put secure show_ime_with_hard_keyboard 0`);
}
}
finally {
console.log(`::endgroup::`);
}
} }
exports.launchEmulator = launchEmulator;
/** /**
* Kills the running emulator on the default port. * Kills the running emulator on the default port.
*/ */
function killEmulator(port) { async function killEmulator(port) {
return __awaiter(this, void 0, void 0, function* () { try {
try { console.log(`::group::Terminate Emulator`);
console.log(`::group::Terminate Emulator`); await adb(port, `emu kill`);
yield adb(port, `emu kill`); }
} catch (error) {
catch (error) { console.log(error instanceof Error ? error.message : error);
console.log(error instanceof Error ? error.message : error); }
} finally {
finally { console.log(`::endgroup::`);
console.log(`::endgroup::`); }
}
});
} }
exports.killEmulator = killEmulator; async function adb(port, command) {
function adb(port, command) { return await exec.exec(`adb -s emulator-${port} ${command}`);
return __awaiter(this, void 0, void 0, function* () {
return yield exec.exec(`adb -s emulator-${port} ${command}`);
});
} }
/** /**
* Wait for emulator to boot. * Wait for emulator to boot.
*/ */
function waitForDevice(port, emulatorBootTimeout) { async function waitForDevice(port, emulatorBootTimeout) {
return __awaiter(this, void 0, void 0, function* () { let booted = false;
let booted = false; let attempts = 0;
let attempts = 0; const retryInterval = 2; // retry every 2 seconds
const retryInterval = 2; // retry every 2 seconds const maxAttempts = emulatorBootTimeout / 2;
const maxAttempts = emulatorBootTimeout / 2; while (!booted) {
while (!booted) { try {
try { let result = '';
let result = ''; await exec.exec(`adb -s emulator-${port} shell getprop sys.boot_completed`, [], {
yield exec.exec(`adb -s emulator-${port} shell getprop sys.boot_completed`, [], { listeners: {
listeners: { stdout: (data) => {
stdout: (data) => { result += data.toString();
result += data.toString();
},
}, },
}); },
if (result.trim() === '1') { });
console.log('Emulator booted.'); if (result.trim() === '1') {
booted = true; console.log('Emulator booted.');
break; booted = true;
} break;
} }
catch (error) {
console.warn(error instanceof Error ? error.message : error);
}
if (attempts < maxAttempts) {
yield delay(retryInterval * 1000);
}
else {
throw new Error(`Timeout waiting for emulator to boot.`);
}
attempts++;
} }
}); catch (error) {
console.warn(error instanceof Error ? error.message : error);
}
if (attempts < maxAttempts) {
await delay(retryInterval * 1000);
}
else {
throw new Error(`Timeout waiting for emulator to boot.`);
}
attempts++;
}
} }
function delay(ms) { function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms)); return new Promise((resolve) => setTimeout(resolve, ms));
+20 -29
View File
@@ -1,48 +1,46 @@
"use strict"; "use strict";
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.checkDiskSize = exports.checkEmulatorBuild = exports.checkEnableHardwareKeyboard = exports.checkDisableLinuxHardwareAcceleration = exports.checkDisableSpellchecker = exports.checkDisableAnimations = exports.checkPort = exports.checkForceAvdCreation = exports.checkChannel = exports.checkArch = exports.checkTarget = exports.checkApiLevel = exports.PREVIEW_API_LEVELS = exports.MAX_PORT = exports.MIN_PORT = exports.VALID_CHANNELS = exports.VALID_ARCHS = exports.VALID_TARGETS = exports.MIN_API_LEVEL = void 0; exports.MAX_PORT = exports.MIN_PORT = exports.VALID_CHANNELS = exports.VALID_ARCHS = exports.MIN_API_LEVEL = void 0;
exports.playstoreTargetSubstitution = playstoreTargetSubstitution;
exports.checkArch = checkArch;
exports.checkChannel = checkChannel;
exports.checkForceAvdCreation = checkForceAvdCreation;
exports.checkPort = checkPort;
exports.checkDisableAnimations = checkDisableAnimations;
exports.checkDisableSpellchecker = checkDisableSpellchecker;
exports.checkDisableLinuxHardwareAcceleration = checkDisableLinuxHardwareAcceleration;
exports.checkEnableHardwareKeyboard = checkEnableHardwareKeyboard;
exports.checkEmulatorBuild = checkEmulatorBuild;
exports.checkDiskSize = checkDiskSize;
exports.MIN_API_LEVEL = 15; exports.MIN_API_LEVEL = 15;
exports.VALID_TARGETS = ['default', 'google_apis', 'aosp_atd', 'google_atd', 'google_apis_playstore', 'android-wear', 'android-wear-cn', 'android-tv', 'google-tv'];
exports.VALID_ARCHS = ['x86', 'x86_64', 'arm64-v8a']; exports.VALID_ARCHS = ['x86', 'x86_64', 'arm64-v8a'];
exports.VALID_CHANNELS = ['stable', 'beta', 'dev', 'canary']; exports.VALID_CHANNELS = ['stable', 'beta', 'dev', 'canary'];
exports.MIN_PORT = 5554; exports.MIN_PORT = 5554;
exports.MAX_PORT = 5584; exports.MAX_PORT = 5584;
exports.PREVIEW_API_LEVELS = ['Tiramisu', 'UpsideDownCake', 'VanillaIceCream']; function playstoreTargetSubstitution(target) {
function checkApiLevel(apiLevel) { // "playstore" is an allowed shorthand for "google_apis_playstore" images
if (exports.PREVIEW_API_LEVELS.some((previewLevel) => apiLevel.startsWith(previewLevel))) // this is idempotent - return same even if run multiple times on same target
return; if (target === 'playstore')
if (isNaN(Number(apiLevel)) || !Number.isInteger(Number(apiLevel))) { return 'google_apis_playstore';
throw new Error(`Unexpected API level: '${apiLevel}'.`); if (target === 'playstore_ps16k')
} return 'google_apis_playstore_ps16k';
if (Number(apiLevel) < exports.MIN_API_LEVEL) { return target;
throw new Error(`Minimum API level supported is ${exports.MIN_API_LEVEL}.`);
}
} }
exports.checkApiLevel = checkApiLevel;
function checkTarget(target) {
if (!exports.VALID_TARGETS.includes(target)) {
throw new Error(`Value for input.target '${target}' is unknown. Supported options: ${exports.VALID_TARGETS}.`);
}
}
exports.checkTarget = checkTarget;
function checkArch(arch) { function checkArch(arch) {
if (!exports.VALID_ARCHS.includes(arch)) { if (!exports.VALID_ARCHS.includes(arch)) {
throw new Error(`Value for input.arch '${arch}' is unknown. Supported options: ${exports.VALID_ARCHS}.`); throw new Error(`Value for input.arch '${arch}' is unknown. Supported options: ${exports.VALID_ARCHS}.`);
} }
} }
exports.checkArch = checkArch;
function checkChannel(channel) { function checkChannel(channel) {
if (!exports.VALID_CHANNELS.includes(channel)) { if (!exports.VALID_CHANNELS.includes(channel)) {
throw new Error(`Value for input.channel '${channel}' is unknown. Supported options: ${exports.VALID_CHANNELS}.`); throw new Error(`Value for input.channel '${channel}' is unknown. Supported options: ${exports.VALID_CHANNELS}.`);
} }
} }
exports.checkChannel = checkChannel;
function checkForceAvdCreation(forceAvdCreation) { function checkForceAvdCreation(forceAvdCreation) {
if (!isValidBoolean(forceAvdCreation)) { if (!isValidBoolean(forceAvdCreation)) {
throw new Error(`Input for input.force-avd-creation should be either 'true' or 'false'.`); throw new Error(`Input for input.force-avd-creation should be either 'true' or 'false'.`);
} }
} }
exports.checkForceAvdCreation = checkForceAvdCreation;
function checkPort(port) { function checkPort(port) {
if (port < exports.MIN_PORT || port > exports.MAX_PORT) { if (port < exports.MIN_PORT || port > exports.MAX_PORT) {
throw new Error(`Emulator port is outside of the supported port range [${exports.MIN_PORT}, ${exports.MAX_PORT}], was ${port}`); throw new Error(`Emulator port is outside of the supported port range [${exports.MIN_PORT}, ${exports.MAX_PORT}], was ${port}`);
@@ -51,37 +49,31 @@ function checkPort(port) {
throw new Error(`Emulator port has to be even, was ${port}`); throw new Error(`Emulator port has to be even, was ${port}`);
} }
} }
exports.checkPort = checkPort;
function checkDisableAnimations(disableAnimations) { function checkDisableAnimations(disableAnimations) {
if (!isValidBoolean(disableAnimations)) { if (!isValidBoolean(disableAnimations)) {
throw new Error(`Input for input.disable-animations should be either 'true' or 'false'.`); throw new Error(`Input for input.disable-animations should be either 'true' or 'false'.`);
} }
} }
exports.checkDisableAnimations = checkDisableAnimations;
function checkDisableSpellchecker(disableSpellchecker) { function checkDisableSpellchecker(disableSpellchecker) {
if (!isValidBoolean(disableSpellchecker)) { if (!isValidBoolean(disableSpellchecker)) {
throw new Error(`Input for input.disable-spellchecker should be either 'true' or 'false'.`); throw new Error(`Input for input.disable-spellchecker should be either 'true' or 'false'.`);
} }
} }
exports.checkDisableSpellchecker = checkDisableSpellchecker;
function checkDisableLinuxHardwareAcceleration(disableLinuxHardwareAcceleration) { function checkDisableLinuxHardwareAcceleration(disableLinuxHardwareAcceleration) {
if (!(isValidBoolean(disableLinuxHardwareAcceleration) || disableLinuxHardwareAcceleration === 'auto')) { if (!(isValidBoolean(disableLinuxHardwareAcceleration) || disableLinuxHardwareAcceleration === 'auto')) {
throw new Error(`Input for input.disable-linux-hw-accel should be either 'true' or 'false' or 'auto'.`); throw new Error(`Input for input.disable-linux-hw-accel should be either 'true' or 'false' or 'auto'.`);
} }
} }
exports.checkDisableLinuxHardwareAcceleration = checkDisableLinuxHardwareAcceleration;
function checkEnableHardwareKeyboard(enableHardwareKeyboard) { function checkEnableHardwareKeyboard(enableHardwareKeyboard) {
if (!isValidBoolean(enableHardwareKeyboard)) { if (!isValidBoolean(enableHardwareKeyboard)) {
throw new Error(`Input for input.enable-hw-keyboard should be either 'true' or 'false'.`); throw new Error(`Input for input.enable-hw-keyboard should be either 'true' or 'false'.`);
} }
} }
exports.checkEnableHardwareKeyboard = checkEnableHardwareKeyboard;
function checkEmulatorBuild(emulatorBuild) { function checkEmulatorBuild(emulatorBuild) {
if (isNaN(Number(emulatorBuild)) || !Number.isInteger(Number(emulatorBuild))) { if (isNaN(Number(emulatorBuild)) || !Number.isInteger(Number(emulatorBuild))) {
throw new Error(`Unexpected emulator build: '${emulatorBuild}'.`); throw new Error(`Unexpected emulator build: '${emulatorBuild}'.`);
} }
} }
exports.checkEmulatorBuild = checkEmulatorBuild;
function isValidBoolean(value) { function isValidBoolean(value) {
return value === 'true' || value === 'false'; return value === 'true' || value === 'false';
} }
@@ -101,4 +93,3 @@ function checkDiskSize(diskSize) {
} }
} }
} }
exports.checkDiskSize = checkDiskSize;
+188 -185
View File
@@ -15,22 +15,23 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
}) : function(o, v) { }) : function(o, v) {
o["default"] = v; o["default"] = v;
}); });
var __importStar = (this && this.__importStar) || function (mod) { var __importStar = (this && this.__importStar) || (function () {
if (mod && mod.__esModule) return mod; var ownKeys = function(o) {
var result = {}; ownKeys = Object.getOwnPropertyNames || function (o) {
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); var ar = [];
__setModuleDefault(result, mod); for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return result; return ar;
}; };
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return ownKeys(o);
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } };
return new (P || (P = Promise))(function (resolve, reject) { return function (mod) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } if (mod && mod.__esModule) return mod;
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } var result = {};
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
step((generator = generator.apply(thisArg, _arguments || [])).next()); __setModuleDefault(result, mod);
}); return result;
}; };
})();
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
const core = __importStar(require("@actions/core")); const core = __importStar(require("@actions/core"));
const sdk_installer_1 = require("./sdk-installer"); const sdk_installer_1 = require("./sdk-installer");
@@ -40,190 +41,192 @@ const exec = __importStar(require("@actions/exec"));
const script_parser_1 = require("./script-parser"); const script_parser_1 = require("./script-parser");
const channel_id_mapper_1 = require("./channel-id-mapper"); const channel_id_mapper_1 = require("./channel-id-mapper");
const fs_1 = require("fs"); const fs_1 = require("fs");
function run() { async function run() {
return __awaiter(this, void 0, void 0, function* () { let port = input_validator_1.MIN_PORT;
let port = input_validator_1.MIN_PORT; try {
try { console.log(`::group::Configure emulator`);
console.log(`::group::Configure emulator`); let linuxSupportKVM = false;
let linuxSupportKVM = false; // only support running on macOS or Linux
// only support running on macOS or Linux if (process.platform !== 'darwin') {
if (process.platform !== 'darwin') { if (process.platform === 'linux') {
if (process.platform === 'linux') {
try {
(0, fs_1.accessSync)('/dev/kvm', fs_1.constants.R_OK | fs_1.constants.W_OK);
linuxSupportKVM = true;
}
catch (_a) {
console.warn(`You're running a Linux VM where hardware acceleration is not available. Please consider using a macOS VM instead to take advantage of native hardware acceleration support provided by HAXM.`);
}
}
else {
throw new Error('Unsupported virtual machine: please use either macos or ubuntu VM.');
}
}
// API level of the platform and system image
const apiLevel = core.getInput('api-level', { required: true });
(0, input_validator_1.checkApiLevel)(apiLevel);
console.log(`API level: ${apiLevel}`);
// target of the system image
const targetInput = core.getInput('target');
const target = targetInput == 'playstore' ? 'google_apis_playstore' : targetInput;
(0, input_validator_1.checkTarget)(target);
console.log(`target: ${target}`);
// CPU architecture of the system image
const arch = core.getInput('arch');
(0, input_validator_1.checkArch)(arch);
console.log(`CPU architecture: ${arch}`);
// Hardware profile used for creating the AVD
const profile = core.getInput('profile');
console.log(`Hardware profile: ${profile}`);
// Number of cores to use for emulator
const cores = core.getInput('cores');
console.log(`Cores: ${cores}`);
// RAM to use for AVD
const ramSize = core.getInput('ram-size');
console.log(`RAM size: ${ramSize}`);
// Heap size to use for AVD
const heapSize = core.getInput('heap-size');
console.log(`Heap size: ${heapSize}`);
// SD card path or size used for creating the AVD
const sdcardPathOrSize = core.getInput('sdcard-path-or-size');
console.log(`SD card path or size: ${sdcardPathOrSize}`);
const diskSize = core.getInput('disk-size');
(0, input_validator_1.checkDiskSize)(diskSize);
console.log(`Disk size: ${diskSize}`);
// custom name used for creating the AVD
const avdName = core.getInput('avd-name');
console.log(`AVD name: ${avdName}`);
// force AVD creation
const forceAvdCreationInput = core.getInput('force-avd-creation');
(0, input_validator_1.checkForceAvdCreation)(forceAvdCreationInput);
const forceAvdCreation = forceAvdCreationInput === 'true';
console.log(`force avd creation: ${forceAvdCreation}`);
// Emulator boot timeout seconds
const emulatorBootTimeout = parseInt(core.getInput('emulator-boot-timeout'), 10);
console.log(`Emulator boot timeout: ${emulatorBootTimeout}`);
// Emulator port to use
port = parseInt(core.getInput('emulator-port'), 10);
(0, input_validator_1.checkPort)(port);
console.log(`emulator port: ${port}`);
// emulator options
const emulatorOptions = core.getInput('emulator-options').trim();
console.log(`emulator options: ${emulatorOptions}`);
// disable animations
const disableAnimationsInput = core.getInput('disable-animations');
(0, input_validator_1.checkDisableAnimations)(disableAnimationsInput);
const disableAnimations = disableAnimationsInput === 'true';
console.log(`disable animations: ${disableAnimations}`);
// disable spellchecker
const disableSpellcheckerInput = core.getInput('disable-spellchecker');
(0, input_validator_1.checkDisableSpellchecker)(disableSpellcheckerInput);
const disableSpellchecker = disableSpellcheckerInput === 'true';
console.log(`disable spellchecker: ${disableSpellchecker}`);
// disable linux hardware acceleration
let disableLinuxHardwareAccelerationInput = core.getInput('disable-linux-hw-accel');
(0, input_validator_1.checkDisableLinuxHardwareAcceleration)(disableLinuxHardwareAccelerationInput);
if (disableLinuxHardwareAccelerationInput === 'auto' && process.platform === 'linux') {
disableLinuxHardwareAccelerationInput = linuxSupportKVM ? 'false' : 'true';
}
const disableLinuxHardwareAcceleration = disableLinuxHardwareAccelerationInput === 'true';
console.log(`disable Linux hardware acceleration: ${disableLinuxHardwareAcceleration}`);
// enable hardware keyboard
const enableHardwareKeyboardInput = core.getInput('enable-hw-keyboard');
(0, input_validator_1.checkEnableHardwareKeyboard)(enableHardwareKeyboardInput);
const enableHardwareKeyboard = enableHardwareKeyboardInput === 'true';
console.log(`enable hardware keyboard: ${enableHardwareKeyboard}`);
// emulator build
const emulatorBuildInput = core.getInput('emulator-build');
if (emulatorBuildInput) {
(0, input_validator_1.checkEmulatorBuild)(emulatorBuildInput);
console.log(`using emulator build: ${emulatorBuildInput}`);
}
const emulatorBuild = !emulatorBuildInput ? undefined : emulatorBuildInput;
// custom working directory
const workingDirectoryInput = core.getInput('working-directory');
if (workingDirectoryInput) {
console.log(`custom working directory: ${workingDirectoryInput}`);
}
const workingDirectory = !workingDirectoryInput ? undefined : workingDirectoryInput;
// version of NDK to install
const ndkInput = core.getInput('ndk');
if (ndkInput) {
console.log(`version of NDK to install: ${ndkInput}`);
}
const ndkVersion = !ndkInput ? undefined : ndkInput;
// version of CMake to install
const cmakeInput = core.getInput('cmake');
if (cmakeInput) {
console.log(`version of CMake to install: ${cmakeInput}`);
}
const cmakeVersion = !cmakeInput ? undefined : cmakeInput;
// channelId (up to and including) to download the SDK packages from
const channelName = core.getInput('channel');
(0, input_validator_1.checkChannel)(channelName);
const channelId = (0, channel_id_mapper_1.getChannelId)(channelName);
console.log(`Channel: ${channelId} (${channelName})`);
// custom script to run
const scriptInput = core.getInput('script', { required: true });
const scripts = (0, script_parser_1.parseScript)(scriptInput);
console.log(`Script:`);
scripts.forEach((script) => __awaiter(this, void 0, void 0, function* () {
console.log(`${script}`);
}));
// custom pre emulator launch script
const preEmulatorLaunchScriptInput = core.getInput('pre-emulator-launch-script');
const preEmulatorLaunchScripts = !preEmulatorLaunchScriptInput ? undefined : (0, script_parser_1.parseScript)(preEmulatorLaunchScriptInput);
console.log(`Pre emulator launch script:`);
preEmulatorLaunchScripts === null || preEmulatorLaunchScripts === void 0 ? void 0 : preEmulatorLaunchScripts.forEach((script) => __awaiter(this, void 0, void 0, function* () {
console.log(`${script}`);
}));
console.log(`::endgroup::`);
// install SDK
yield (0, sdk_installer_1.installAndroidSdk)(apiLevel, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion);
// execute pre emulator launch script if set
if (preEmulatorLaunchScripts !== undefined) {
console.log(`::group::Run pre emulator launch script`);
try { try {
for (const preEmulatorLaunchScript of preEmulatorLaunchScripts) { (0, fs_1.accessSync)('/dev/kvm', fs_1.constants.R_OK | fs_1.constants.W_OK);
// use array form to avoid various quote escaping problems linuxSupportKVM = true;
// caused by exec(`sh -c "${preEmulatorLaunchScript}"`)
yield exec.exec('sh', ['-c', preEmulatorLaunchScript], {
cwd: workingDirectory,
});
}
} }
catch (error) { catch {
core.setFailed(error instanceof Error ? error.message : error); console.warn(`You're running a Linux VM where hardware acceleration is not available. Please consider using a macOS VM instead to take advantage of native hardware acceleration support provided by HAXM.`);
} }
console.log(`::endgroup::`);
} }
// launch an emulator else {
yield (0, emulator_manager_1.launchEmulator)(apiLevel, target, arch, profile, cores, ramSize, heapSize, sdcardPathOrSize, diskSize, avdName, forceAvdCreation, emulatorBootTimeout, port, emulatorOptions, disableAnimations, disableSpellchecker, disableLinuxHardwareAcceleration, enableHardwareKeyboard); throw new Error('Unsupported virtual machine: please use either macos or ubuntu VM.');
// execute the custom script }
}
// API level of the platform and system image
const apiLevel = core.getInput('api-level', { required: true });
console.log(`API level: ${apiLevel}`);
let systemImageApiLevel = core.getInput('system-image-api-level');
if (!systemImageApiLevel) {
systemImageApiLevel = apiLevel;
}
console.log(`System image API level: ${systemImageApiLevel}`);
// target of the system image
const target = (0, input_validator_1.playstoreTargetSubstitution)(core.getInput('target'));
console.log(`target: ${target}`);
// CPU architecture of the system image
const arch = core.getInput('arch');
(0, input_validator_1.checkArch)(arch);
console.log(`CPU architecture: ${arch}`);
// Hardware profile used for creating the AVD
const profile = core.getInput('profile');
console.log(`Hardware profile: ${profile}`);
// Number of cores to use for emulator
const cores = core.getInput('cores');
console.log(`Cores: ${cores}`);
// RAM to use for AVD
const ramSize = core.getInput('ram-size');
console.log(`RAM size: ${ramSize}`);
// Heap size to use for AVD
const heapSize = core.getInput('heap-size');
console.log(`Heap size: ${heapSize}`);
// SD card path or size used for creating the AVD
const sdcardPathOrSize = core.getInput('sdcard-path-or-size');
console.log(`SD card path or size: ${sdcardPathOrSize}`);
const diskSize = core.getInput('disk-size');
(0, input_validator_1.checkDiskSize)(diskSize);
console.log(`Disk size: ${diskSize}`);
// custom name used for creating the AVD
const avdName = core.getInput('avd-name');
console.log(`AVD name: ${avdName}`);
// force AVD creation
const forceAvdCreationInput = core.getInput('force-avd-creation');
(0, input_validator_1.checkForceAvdCreation)(forceAvdCreationInput);
const forceAvdCreation = forceAvdCreationInput === 'true';
console.log(`force avd creation: ${forceAvdCreation}`);
// Emulator boot timeout seconds
const emulatorBootTimeout = parseInt(core.getInput('emulator-boot-timeout'), 10);
console.log(`Emulator boot timeout: ${emulatorBootTimeout}`);
// Emulator port to use
port = parseInt(core.getInput('emulator-port'), 10);
(0, input_validator_1.checkPort)(port);
console.log(`emulator port: ${port}`);
// emulator options
const emulatorOptions = core.getInput('emulator-options').trim();
console.log(`emulator options: ${emulatorOptions}`);
// disable animations
const disableAnimationsInput = core.getInput('disable-animations');
(0, input_validator_1.checkDisableAnimations)(disableAnimationsInput);
const disableAnimations = disableAnimationsInput === 'true';
console.log(`disable animations: ${disableAnimations}`);
// disable spellchecker
const disableSpellcheckerInput = core.getInput('disable-spellchecker');
(0, input_validator_1.checkDisableSpellchecker)(disableSpellcheckerInput);
const disableSpellchecker = disableSpellcheckerInput === 'true';
console.log(`disable spellchecker: ${disableSpellchecker}`);
// disable linux hardware acceleration
let disableLinuxHardwareAccelerationInput = core.getInput('disable-linux-hw-accel');
(0, input_validator_1.checkDisableLinuxHardwareAcceleration)(disableLinuxHardwareAccelerationInput);
if (disableLinuxHardwareAccelerationInput === 'auto' && process.platform === 'linux') {
disableLinuxHardwareAccelerationInput = linuxSupportKVM ? 'false' : 'true';
}
const disableLinuxHardwareAcceleration = disableLinuxHardwareAccelerationInput === 'true';
console.log(`disable Linux hardware acceleration: ${disableLinuxHardwareAcceleration}`);
// enable hardware keyboard
const enableHardwareKeyboardInput = core.getInput('enable-hw-keyboard');
(0, input_validator_1.checkEnableHardwareKeyboard)(enableHardwareKeyboardInput);
const enableHardwareKeyboard = enableHardwareKeyboardInput === 'true';
console.log(`enable hardware keyboard: ${enableHardwareKeyboard}`);
// emulator build
const emulatorBuildInput = core.getInput('emulator-build');
if (emulatorBuildInput) {
(0, input_validator_1.checkEmulatorBuild)(emulatorBuildInput);
console.log(`using emulator build: ${emulatorBuildInput}`);
}
const emulatorBuild = !emulatorBuildInput ? undefined : emulatorBuildInput;
// custom working directory
const workingDirectoryInput = core.getInput('working-directory');
if (workingDirectoryInput) {
console.log(`custom working directory: ${workingDirectoryInput}`);
}
const workingDirectory = !workingDirectoryInput ? undefined : workingDirectoryInput;
// version of NDK to install
const ndkInput = core.getInput('ndk');
if (ndkInput) {
console.log(`version of NDK to install: ${ndkInput}`);
}
const ndkVersion = !ndkInput ? undefined : ndkInput;
// version of CMake to install
const cmakeInput = core.getInput('cmake');
if (cmakeInput) {
console.log(`version of CMake to install: ${cmakeInput}`);
}
const cmakeVersion = !cmakeInput ? undefined : cmakeInput;
// channelId (up to and including) to download the SDK packages from
const channelName = core.getInput('channel');
(0, input_validator_1.checkChannel)(channelName);
const channelId = (0, channel_id_mapper_1.getChannelId)(channelName);
console.log(`Channel: ${channelId} (${channelName})`);
// custom script to run
const scriptInput = core.getInput('script', { required: true });
const scripts = (0, script_parser_1.parseScript)(scriptInput);
console.log(`Script:`);
scripts.forEach(async (script) => {
console.log(`${script}`);
});
// custom pre emulator launch script
const preEmulatorLaunchScriptInput = core.getInput('pre-emulator-launch-script');
const preEmulatorLaunchScripts = !preEmulatorLaunchScriptInput ? undefined : (0, script_parser_1.parseScript)(preEmulatorLaunchScriptInput);
console.log(`Pre emulator launch script:`);
preEmulatorLaunchScripts?.forEach(async (script) => {
console.log(`${script}`);
});
console.log(`::endgroup::`);
// install SDK
await (0, sdk_installer_1.installAndroidSdk)(apiLevel, systemImageApiLevel, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion);
// create AVD
await (0, emulator_manager_1.createAvd)(arch, avdName, cores, diskSize, enableHardwareKeyboard, forceAvdCreation, heapSize, profile, ramSize, sdcardPathOrSize, systemImageApiLevel, target);
// execute pre emulator launch script if set
if (preEmulatorLaunchScripts !== undefined) {
console.log(`::group::Run pre emulator launch script`);
try { try {
// move to custom working directory if set for (const preEmulatorLaunchScript of preEmulatorLaunchScripts) {
if (workingDirectory) {
process.chdir(workingDirectory);
}
for (const script of scripts) {
// use array form to avoid various quote escaping problems // use array form to avoid various quote escaping problems
// caused by exec(`sh -c "${script}"`) // caused by exec(`sh -c "${preEmulatorLaunchScript}"`)
yield exec.exec('sh', ['-c', script], { await exec.exec('sh', ['-c', preEmulatorLaunchScript], {
env: Object.assign(Object.assign({}, process.env), { EMULATOR_PORT: `${port}`, ANDROID_SERIAL: `emulator-${port}` }), cwd: workingDirectory,
}); });
} }
} }
catch (error) { catch (error) {
core.setFailed(error instanceof Error ? error.message : error); core.setFailed(error instanceof Error ? error.message : error);
} }
// finally kill the emulator console.log(`::endgroup::`);
yield (0, emulator_manager_1.killEmulator)(port); }
// launch an emulator
await (0, emulator_manager_1.launchEmulator)(avdName, disableAnimations, disableLinuxHardwareAcceleration, disableSpellchecker, emulatorBootTimeout, emulatorOptions, enableHardwareKeyboard, port);
// execute the custom script
try {
// move to custom working directory if set
if (workingDirectory) {
process.chdir(workingDirectory);
}
for (const script of scripts) {
// use array form to avoid various quote escaping problems
// caused by exec(`sh -c "${script}"`)
await exec.exec('sh', ['-c', script], {
env: { ...process.env, EMULATOR_PORT: `${port}`, ANDROID_SERIAL: `emulator-${port}` },
});
}
} }
catch (error) { catch (error) {
// kill the emulator so the action can exit
yield (0, emulator_manager_1.killEmulator)(port);
core.setFailed(error instanceof Error ? error.message : error); core.setFailed(error instanceof Error ? error.message : error);
} }
}); // finally kill the emulator
await (0, emulator_manager_1.killEmulator)(port);
}
catch (error) {
// kill the emulator so the action can exit
await (0, emulator_manager_1.killEmulator)(port);
core.setFailed(error instanceof Error ? error.message : error);
}
} }
run(); run();
+1 -2
View File
@@ -1,6 +1,6 @@
"use strict"; "use strict";
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.parseScript = void 0; exports.parseScript = parseScript;
/** /**
* Convert a (potentially multi-line) script to an array of single-line script(s). * Convert a (potentially multi-line) script to an array of single-line script(s).
*/ */
@@ -17,4 +17,3 @@ function parseScript(rawScript) {
} }
return scripts; return scripts;
} }
exports.parseScript = parseScript;
+75 -77
View File
@@ -15,99 +15,97 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
}) : function(o, v) { }) : function(o, v) {
o["default"] = v; o["default"] = v;
}); });
var __importStar = (this && this.__importStar) || function (mod) { var __importStar = (this && this.__importStar) || (function () {
if (mod && mod.__esModule) return mod; var ownKeys = function(o) {
var result = {}; ownKeys = Object.getOwnPropertyNames || function (o) {
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); var ar = [];
__setModuleDefault(result, mod); for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return result; return ar;
}; };
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return ownKeys(o);
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } };
return new (P || (P = Promise))(function (resolve, reject) { return function (mod) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } if (mod && mod.__esModule) return mod;
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } var result = {};
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
step((generator = generator.apply(thisArg, _arguments || [])).next()); __setModuleDefault(result, mod);
}); return result;
}; };
})();
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.installAndroidSdk = void 0; exports.installAndroidSdk = installAndroidSdk;
const core = __importStar(require("@actions/core")); const core = __importStar(require("@actions/core"));
const exec = __importStar(require("@actions/exec")); const exec = __importStar(require("@actions/exec"));
const io = __importStar(require("@actions/io")); const io = __importStar(require("@actions/io"));
const tc = __importStar(require("@actions/tool-cache")); const tc = __importStar(require("@actions/tool-cache"));
const fs = __importStar(require("fs")); const fs = __importStar(require("fs"));
const BUILD_TOOLS_VERSION = '35.0.0'; const BUILD_TOOLS_VERSION = '37.0.0';
// SDK command-line tools 16.0 // SDK command-line tools 20.0
const CMDLINE_TOOLS_URL_MAC = 'https://dl.google.com/android/repository/commandlinetools-mac-12266719_latest.zip'; const CMDLINE_TOOLS_URL_MAC = 'https://dl.google.com/android/repository/commandlinetools-mac-14742923_latest.zip';
const CMDLINE_TOOLS_URL_LINUX = 'https://dl.google.com/android/repository/commandlinetools-linux-12266719_latest.zip'; const CMDLINE_TOOLS_URL_LINUX = 'https://dl.google.com/android/repository/commandlinetools-linux-14742923_latest.zip';
/** /**
* Installs & updates the Android SDK for the macOS platform, including SDK platform for the chosen API level, latest build tools, platform tools, Android Emulator, * Installs & updates the Android SDK for the macOS platform, including SDK platform for the chosen API level, latest build tools, platform tools, Android Emulator,
* and the system image for the chosen API level, CPU arch, and target. * and the system image for the chosen API level, CPU arch, and target.
*/ */
function installAndroidSdk(apiLevel, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion) { async function installAndroidSdk(apiLevel, systemImageApiLevel, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion) {
return __awaiter(this, void 0, void 0, function* () { try {
try { console.log(`::group::Install Android SDK`);
console.log(`::group::Install Android SDK`); const isOnMac = process.platform === 'darwin';
const isOnMac = process.platform === 'darwin'; const isArm = process.arch === 'arm64';
const isArm = process.arch === 'arm64'; const cmdlineToolsPath = `${process.env.ANDROID_HOME}/cmdline-tools`;
const cmdlineToolsPath = `${process.env.ANDROID_HOME}/cmdline-tools`; if (!fs.existsSync(cmdlineToolsPath)) {
if (!fs.existsSync(cmdlineToolsPath)) { console.log('Installing new cmdline-tools.');
console.log('Installing new cmdline-tools.'); const sdkUrl = isOnMac ? CMDLINE_TOOLS_URL_MAC : CMDLINE_TOOLS_URL_LINUX;
const sdkUrl = isOnMac ? CMDLINE_TOOLS_URL_MAC : CMDLINE_TOOLS_URL_LINUX; const downloadPath = await tc.downloadTool(sdkUrl);
const downloadPath = yield tc.downloadTool(sdkUrl); await tc.extractZip(downloadPath, cmdlineToolsPath);
yield tc.extractZip(downloadPath, cmdlineToolsPath); await io.mv(`${cmdlineToolsPath}/cmdline-tools`, `${cmdlineToolsPath}/latest`);
yield io.mv(`${cmdlineToolsPath}/cmdline-tools`, `${cmdlineToolsPath}/latest`); }
} // add paths for commandline-tools and platform-tools
// add paths for commandline-tools and platform-tools core.addPath(`${cmdlineToolsPath}/latest:${cmdlineToolsPath}/latest/bin:${process.env.ANDROID_HOME}/platform-tools`);
core.addPath(`${cmdlineToolsPath}/latest:${cmdlineToolsPath}/latest/bin:${process.env.ANDROID_HOME}/platform-tools`); // set standard AVD path
// set standard AVD path await io.mkdirP(`${process.env.HOME}/.android/avd`);
yield io.mkdirP(`${process.env.HOME}/.android/avd`); core.exportVariable('ANDROID_AVD_HOME', `${process.env.HOME}/.android/avd`);
core.exportVariable('ANDROID_AVD_HOME', `${process.env.HOME}/.android/avd`); // accept all Android SDK licenses
// accept all Android SDK licenses await exec.exec(`sh -c \\"yes | sdkmanager --licenses > /dev/null"`);
yield exec.exec(`sh -c \\"yes | sdkmanager --licenses > /dev/null"`); console.log('Installing latest build tools, platform tools, and platform.');
console.log('Installing latest build tools, platform tools, and platform.'); await exec.exec(`sh -c \\"sdkmanager --install 'build-tools;${BUILD_TOOLS_VERSION}' platform-tools 'platforms;android-${apiLevel}'> /dev/null"`);
yield exec.exec(`sh -c \\"sdkmanager --install 'build-tools;${BUILD_TOOLS_VERSION}' platform-tools 'platforms;android-${apiLevel}'> /dev/null"`); console.log('Installing latest emulator.');
console.log('Installing latest emulator.'); await exec.exec(`sh -c \\"sdkmanager --install emulator --channel=${channelId} > /dev/null"`);
yield exec.exec(`sh -c \\"sdkmanager --install emulator --channel=${channelId} > /dev/null"`); if (emulatorBuild) {
if (emulatorBuild) { console.log(`Installing emulator build ${emulatorBuild}.`);
console.log(`Installing emulator build ${emulatorBuild}.`); // TODO find out the correct download URLs for all build ids
// TODO find out the correct download URLs for all build ids var downloadUrlSuffix;
var downloadUrlSuffix; const majorBuildVersion = Number(emulatorBuild);
const majorBuildVersion = Number(emulatorBuild); if (majorBuildVersion >= 8000000) {
if (majorBuildVersion >= 8000000) { if (isArm) {
if (isArm) { downloadUrlSuffix = `_aarch64-${emulatorBuild}`;
downloadUrlSuffix = `_aarch64-${emulatorBuild}`;
}
else {
downloadUrlSuffix = `_x64-${emulatorBuild}`;
}
}
else if (majorBuildVersion >= 7000000) {
downloadUrlSuffix = `_x64-${emulatorBuild}`;
} }
else { else {
downloadUrlSuffix = `-${emulatorBuild}`; downloadUrlSuffix = `_x64-${emulatorBuild}`;
} }
yield exec.exec(`curl -fo emulator.zip https://dl.google.com/android/repository/emulator-${isOnMac ? 'darwin' : 'linux'}${downloadUrlSuffix}.zip`);
yield exec.exec(`unzip -o -q emulator.zip -d ${process.env.ANDROID_HOME}`);
yield io.rmRF('emulator.zip');
} }
console.log('Installing system images.'); else if (majorBuildVersion >= 7000000) {
yield exec.exec(`sh -c \\"sdkmanager --install 'system-images;android-${apiLevel};${target};${arch}' --channel=${channelId} > /dev/null"`); downloadUrlSuffix = `_x64-${emulatorBuild}`;
if (ndkVersion) {
console.log(`Installing NDK ${ndkVersion}.`);
yield exec.exec(`sh -c \\"sdkmanager --install 'ndk;${ndkVersion}' --channel=${channelId} > /dev/null"`);
} }
if (cmakeVersion) { else {
console.log(`Installing CMake ${cmakeVersion}.`); downloadUrlSuffix = `-${emulatorBuild}`;
yield exec.exec(`sh -c \\"sdkmanager --install 'cmake;${cmakeVersion}' --channel=${channelId} > /dev/null"`);
} }
await exec.exec(`curl -fo emulator.zip https://dl.google.com/android/repository/emulator-${isOnMac ? 'darwin' : 'linux'}${downloadUrlSuffix}.zip`);
await exec.exec(`unzip -o -q emulator.zip -d ${process.env.ANDROID_HOME}`);
await io.rmRF('emulator.zip');
} }
finally { console.log('Installing system images.');
console.log(`::endgroup::`); await exec.exec(`sh -c \\"sdkmanager --install 'system-images;android-${systemImageApiLevel};${target};${arch}' --channel=${channelId} > /dev/null"`);
if (ndkVersion) {
console.log(`Installing NDK ${ndkVersion}.`);
await exec.exec(`sh -c \\"sdkmanager --install 'ndk;${ndkVersion}' --channel=${channelId} > /dev/null"`);
} }
}); if (cmakeVersion) {
console.log(`Installing CMake ${cmakeVersion}.`);
await exec.exec(`sh -c \\"sdkmanager --install 'cmake;${cmakeVersion}' --channel=${channelId} > /dev/null"`);
}
}
finally {
console.log(`::endgroup::`);
}
} }
exports.installAndroidSdk = installAndroidSdk;
+3976 -3117
View File
File diff suppressed because it is too large Load Diff
+14 -14
View File
@@ -19,7 +19,7 @@
"setup" "setup"
], ],
"engines": { "engines": {
"node": ">=20" "node": ">=24"
}, },
"author": "Reactive Circus", "author": "Reactive Circus",
"license": "MIT", "license": "MIT",
@@ -31,18 +31,18 @@
"set-value": "^4.1.0" "set-value": "^4.1.0"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^29.2.0", "@types/jest": "^29.5.12",
"@types/node": "^20.11.0", "@types/node": "^24.1.0",
"@typescript-eslint/eslint-plugin": "^5.41.0", "@typescript-eslint/eslint-plugin": "^8.57.0",
"@typescript-eslint/parser": "^5.41.0", "@typescript-eslint/parser": "^8.57.0",
"eslint": "^8.26.0", "eslint": "^8.57.0",
"eslint-config-prettier": "^8.5.0", "eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.26.0", "eslint-plugin-import": "^2.29.1",
"eslint-plugin-prettier": "^4.2.1", "eslint-plugin-prettier": "^5.1.3",
"jest": "^29.2.2", "jest": "^29.7.0",
"jest-circus": "^29.2.2", "jest-circus": "^29.7.0",
"prettier": "^2.7.1", "prettier": "^3.3.3",
"ts-jest": "^29.0.3", "ts-jest": "^29.2.5",
"typescript": "^4.8.4" "typescript": "^5.5.4"
} }
} }
+53 -34
View File
@@ -2,30 +2,24 @@ import * as exec from '@actions/exec';
import * as fs from 'fs'; import * as fs from 'fs';
/** /**
* Creates and launches a new AVD instance with the specified configurations. * Creates a new AVD instance with the specified configurations.
*/ */
export async function launchEmulator( export async function createAvd(
apiLevel: string,
target: string,
arch: string, arch: string,
profile: string,
cores: string,
ramSize: string,
heapSize: string,
sdcardPathOrSize: string,
diskSize: string,
avdName: string, avdName: string,
cores: string,
diskSize: string,
enableHardwareKeyboard: boolean,
forceAvdCreation: boolean, forceAvdCreation: boolean,
emulatorBootTimeout: number, heapSize: string,
port: number, profile: string,
emulatorOptions: string, ramSize: string,
disableAnimations: boolean, sdcardPathOrSize: string,
disableSpellChecker: boolean, systemImageApiLevel: string,
disableLinuxHardwareAcceleration: boolean, target: string,
enableHardwareKeyboard: boolean
): Promise<void> { ): Promise<void> {
try { try {
console.log(`::group::Launch Emulator`); console.log(`::group::Create AVD`);
// create a new AVD if AVD directory does not already exist or forceAvdCreation is true // create a new AVD if AVD directory does not already exist or forceAvdCreation is true
const avdPath = `${process.env.ANDROID_AVD_HOME}/${avdName}.avd`; const avdPath = `${process.env.ANDROID_AVD_HOME}/${avdName}.avd`;
if (!fs.existsSync(avdPath) || forceAvdCreation) { if (!fs.existsSync(avdPath) || forceAvdCreation) {
@@ -33,29 +27,54 @@ export async function launchEmulator(
const sdcardPathOrSizeOption = sdcardPathOrSize.trim() !== '' ? `--sdcard '${sdcardPathOrSize}'` : ''; const sdcardPathOrSizeOption = sdcardPathOrSize.trim() !== '' ? `--sdcard '${sdcardPathOrSize}'` : '';
console.log(`Creating AVD.`); console.log(`Creating AVD.`);
await exec.exec( await exec.exec(
`sh -c \\"echo no | avdmanager create avd --force -n "${avdName}" --abi '${target}/${arch}' --package 'system-images;android-${apiLevel};${target};${arch}' ${profileOption} ${sdcardPathOrSizeOption}"` `sh -c \\"echo no | avdmanager create avd --force -n "${avdName}" --package 'system-images;android-${systemImageApiLevel};${target};${arch}' ${profileOption} ${sdcardPathOrSizeOption}"`,
); );
} }
if (cores) { if (cores || ramSize || heapSize || enableHardwareKeyboard || diskSize) {
await exec.exec(`sh -c \\"printf 'hw.cpu.ncore=${cores}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`); const configEntries: string[] = [];
}
if (ramSize) { if (cores) {
await exec.exec(`sh -c \\"printf 'hw.ramSize=${ramSize}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`); configEntries.push(`hw.cpu.ncore=${cores}`);
} }
if (ramSize) {
configEntries.push(`hw.ramSize=${ramSize}`);
}
if (heapSize) {
configEntries.push(`hw.heapSize=${heapSize}`);
}
if (enableHardwareKeyboard) {
configEntries.push('hw.keyboard=yes');
}
if (diskSize) {
configEntries.push(`disk.dataPartition.size=${diskSize}`);
}
if (heapSize) { if (configEntries.length > 0) {
await exec.exec(`sh -c \\"printf 'hw.heapSize=${heapSize}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`); const configContent = configEntries.join('\\n') + '\\n';
await exec.exec(`sh -c \\"printf '${configContent}' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini"`);
}
} }
} finally {
console.log(`::endgroup::`);
}
}
if (enableHardwareKeyboard) { /**
await exec.exec(`sh -c \\"printf 'hw.keyboard=yes\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`); * Launches an existing AVD instance with the specified configurations.
} */
export async function launchEmulator(
if (diskSize) { avdName: string,
await exec.exec(`sh -c \\"printf 'disk.dataPartition.size=${diskSize}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`); disableAnimations: boolean,
} disableLinuxHardwareAcceleration: boolean,
disableSpellChecker: boolean,
emulatorBootTimeout: number,
emulatorOptions: string,
enableHardwareKeyboard: boolean,
port: number,
): Promise<void> {
try {
console.log(`::group::Launch Emulator`);
// turn off hardware acceleration on Linux // turn off hardware acceleration on Linux
if (process.platform === 'linux' && disableLinuxHardwareAcceleration) { if (process.platform === 'linux' && disableLinuxHardwareAcceleration) {
+6 -16
View File
@@ -1,25 +1,15 @@
export const MIN_API_LEVEL = 15; export const MIN_API_LEVEL = 15;
export const VALID_TARGETS: Array<string> = ['default', 'google_apis', 'aosp_atd', 'google_atd', 'google_apis_playstore', 'android-wear', 'android-wear-cn', 'android-tv', 'google-tv'];
export const VALID_ARCHS: Array<string> = ['x86', 'x86_64', 'arm64-v8a']; export const VALID_ARCHS: Array<string> = ['x86', 'x86_64', 'arm64-v8a'];
export const VALID_CHANNELS: Array<string> = ['stable', 'beta', 'dev', 'canary']; export const VALID_CHANNELS: Array<string> = ['stable', 'beta', 'dev', 'canary'];
export const MIN_PORT = 5554; export const MIN_PORT = 5554;
export const MAX_PORT = 5584; export const MAX_PORT = 5584;
export const PREVIEW_API_LEVELS: Array<string> = ['Tiramisu', 'UpsideDownCake', 'VanillaIceCream'];
export function checkApiLevel(apiLevel: string): void { export function playstoreTargetSubstitution(target: string): string {
if (PREVIEW_API_LEVELS.some((previewLevel) => apiLevel.startsWith(previewLevel))) return; // "playstore" is an allowed shorthand for "google_apis_playstore" images
if (isNaN(Number(apiLevel)) || !Number.isInteger(Number(apiLevel))) { // this is idempotent - return same even if run multiple times on same target
throw new Error(`Unexpected API level: '${apiLevel}'.`); if (target === 'playstore') return 'google_apis_playstore';
} if (target === 'playstore_ps16k') return 'google_apis_playstore_ps16k';
if (Number(apiLevel) < MIN_API_LEVEL) { return target;
throw new Error(`Minimum API level supported is ${MIN_API_LEVEL}.`);
}
}
export function checkTarget(target: string): void {
if (!VALID_TARGETS.includes(target)) {
throw new Error(`Value for input.target '${target}' is unknown. Supported options: ${VALID_TARGETS}.`);
}
} }
export function checkArch(arch: string): void { export function checkArch(arch: string): void {
+15 -29
View File
@@ -1,8 +1,6 @@
import * as core from '@actions/core'; import * as core from '@actions/core';
import { installAndroidSdk } from './sdk-installer'; import { installAndroidSdk } from './sdk-installer';
import { import {
checkApiLevel,
checkTarget,
checkArch, checkArch,
checkDisableAnimations, checkDisableAnimations,
checkEmulatorBuild, checkEmulatorBuild,
@@ -13,9 +11,10 @@ import {
checkEnableHardwareKeyboard, checkEnableHardwareKeyboard,
checkDiskSize, checkDiskSize,
checkPort, checkPort,
playstoreTargetSubstitution,
MIN_PORT, MIN_PORT,
} from './input-validator'; } from './input-validator';
import { launchEmulator, killEmulator } from './emulator-manager'; import { createAvd, launchEmulator, killEmulator } from './emulator-manager';
import * as exec from '@actions/exec'; import * as exec from '@actions/exec';
import { parseScript } from './script-parser'; import { parseScript } from './script-parser';
import { getChannelId } from './channel-id-mapper'; import { getChannelId } from './channel-id-mapper';
@@ -34,7 +33,7 @@ async function run() {
linuxSupportKVM = true; linuxSupportKVM = true;
} catch { } catch {
console.warn( console.warn(
`You're running a Linux VM where hardware acceleration is not available. Please consider using a macOS VM instead to take advantage of native hardware acceleration support provided by HAXM.` `You're running a Linux VM where hardware acceleration is not available. Please consider using a macOS VM instead to take advantage of native hardware acceleration support provided by HAXM.`,
); );
} }
} else { } else {
@@ -44,13 +43,16 @@ async function run() {
// API level of the platform and system image // API level of the platform and system image
const apiLevel = core.getInput('api-level', { required: true }); const apiLevel = core.getInput('api-level', { required: true });
checkApiLevel(apiLevel);
console.log(`API level: ${apiLevel}`); console.log(`API level: ${apiLevel}`);
let systemImageApiLevel = core.getInput('system-image-api-level');
if (!systemImageApiLevel) {
systemImageApiLevel = apiLevel;
}
console.log(`System image API level: ${systemImageApiLevel}`);
// target of the system image // target of the system image
const targetInput = core.getInput('target'); const target = playstoreTargetSubstitution(core.getInput('target'));
const target = targetInput == 'playstore' ? 'google_apis_playstore' : targetInput;
checkTarget(target);
console.log(`target: ${target}`); console.log(`target: ${target}`);
// CPU architecture of the system image // CPU architecture of the system image
@@ -185,7 +187,10 @@ async function run() {
console.log(`::endgroup::`); console.log(`::endgroup::`);
// install SDK // install SDK
await installAndroidSdk(apiLevel, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion); await installAndroidSdk(apiLevel, systemImageApiLevel, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion);
// create AVD
await createAvd(arch, avdName, cores, diskSize, enableHardwareKeyboard, forceAvdCreation, heapSize, profile, ramSize, sdcardPathOrSize, systemImageApiLevel, target);
// execute pre emulator launch script if set // execute pre emulator launch script if set
if (preEmulatorLaunchScripts !== undefined) { if (preEmulatorLaunchScripts !== undefined) {
@@ -205,26 +210,7 @@ async function run() {
} }
// launch an emulator // launch an emulator
await launchEmulator( await launchEmulator(avdName, disableAnimations, disableLinuxHardwareAcceleration, disableSpellchecker, emulatorBootTimeout, emulatorOptions, enableHardwareKeyboard, port);
apiLevel,
target,
arch,
profile,
cores,
ramSize,
heapSize,
sdcardPathOrSize,
diskSize,
avdName,
forceAvdCreation,
emulatorBootTimeout,
port,
emulatorOptions,
disableAnimations,
disableSpellchecker,
disableLinuxHardwareAcceleration,
enableHardwareKeyboard
);
// execute the custom script // execute the custom script
try { try {
+15 -6
View File
@@ -4,16 +4,25 @@ import * as io from '@actions/io';
import * as tc from '@actions/tool-cache'; import * as tc from '@actions/tool-cache';
import * as fs from 'fs'; import * as fs from 'fs';
const BUILD_TOOLS_VERSION = '35.0.0'; const BUILD_TOOLS_VERSION = '37.0.0';
// SDK command-line tools 16.0 // SDK command-line tools 20.0
const CMDLINE_TOOLS_URL_MAC = 'https://dl.google.com/android/repository/commandlinetools-mac-12266719_latest.zip'; const CMDLINE_TOOLS_URL_MAC = 'https://dl.google.com/android/repository/commandlinetools-mac-14742923_latest.zip';
const CMDLINE_TOOLS_URL_LINUX = 'https://dl.google.com/android/repository/commandlinetools-linux-12266719_latest.zip'; const CMDLINE_TOOLS_URL_LINUX = 'https://dl.google.com/android/repository/commandlinetools-linux-14742923_latest.zip';
/** /**
* Installs & updates the Android SDK for the macOS platform, including SDK platform for the chosen API level, latest build tools, platform tools, Android Emulator, * Installs & updates the Android SDK for the macOS platform, including SDK platform for the chosen API level, latest build tools, platform tools, Android Emulator,
* and the system image for the chosen API level, CPU arch, and target. * and the system image for the chosen API level, CPU arch, and target.
*/ */
export async function installAndroidSdk(apiLevel: string, target: string, arch: string, channelId: number, emulatorBuild?: string, ndkVersion?: string, cmakeVersion?: string): Promise<void> { export async function installAndroidSdk(
apiLevel: string,
systemImageApiLevel: String,
target: string,
arch: string,
channelId: number,
emulatorBuild?: string,
ndkVersion?: string,
cmakeVersion?: string,
): Promise<void> {
try { try {
console.log(`::group::Install Android SDK`); console.log(`::group::Install Android SDK`);
const isOnMac = process.platform === 'darwin'; const isOnMac = process.platform === 'darwin';
@@ -66,7 +75,7 @@ export async function installAndroidSdk(apiLevel: string, target: string, arch:
await io.rmRF('emulator.zip'); await io.rmRF('emulator.zip');
} }
console.log('Installing system images.'); console.log('Installing system images.');
await exec.exec(`sh -c \\"sdkmanager --install 'system-images;android-${apiLevel};${target};${arch}' --channel=${channelId} > /dev/null"`); await exec.exec(`sh -c \\"sdkmanager --install 'system-images;android-${systemImageApiLevel};${target};${arch}' --channel=${channelId} > /dev/null"`);
if (ndkVersion) { if (ndkVersion) {
console.log(`Installing NDK ${ndkVersion}.`); console.log(`Installing NDK ${ndkVersion}.`);
-36
View File
@@ -1,36 +0,0 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
android {
namespace = "com.example.testapp"
compileSdkVersion 35
buildToolsVersion "35.0.0"
defaultConfig {
applicationId "com.example.testapp"
minSdkVersion 21
targetSdkVersion 35
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
}
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
compilerOptions {
jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.7.0'
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
}
+36
View File
@@ -0,0 +1,36 @@
plugins {
id("com.android.application")
}
android {
namespace = "com.example.testapp"
compileSdk = 37
buildToolsVersion = "37.0.0"
defaultConfig {
applicationId = "com.example.testapp"
minSdk = 23
targetSdk = 37
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
}
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
compilerOptions {
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
}
}
dependencies {
implementation("androidx.appcompat:appcompat:1.7.1")
androidTestImplementation("androidx.test.ext:junit:1.3.0")
androidTestImplementation("androidx.test.espresso:espresso-core:3.7.0")
}
-26
View File
@@ -1,26 +0,0 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.7.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.20"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
+5
View File
@@ -0,0 +1,5 @@
plugins {
id("com.android.application") version "9.1.0" apply false
id("org.jetbrains.kotlin.android") version "2.3.10" apply false
}
-2
View File
@@ -5,5 +5,3 @@ org.gradle.caching=true
# Kotlin code style # Kotlin code style
kotlin.code.style=official kotlin.code.style=official
# Enable AndroidX
android.useAndroidX=true
Binary file not shown.
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
+7 -8
View File
@@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
# #
# Copyright © 2015-2021 the original authors. # Copyright © 2015 the original authors.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# #
# SPDX-License-Identifier: Apache-2.0
#
############################################################################## ##############################################################################
# #
@@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop. # Darwin, MinGW, and NonStop.
# #
# (3) This script is generated from the Groovy template # (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 # https://github.com/gradle/gradle/blob/b631911858264c0b6e4d6603d677ff5218766cee/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project. # within the Gradle project.
# #
# You can find Gradle at https://github.com/gradle/gradle/. # You can find Gradle at https://github.com/gradle/gradle/.
@@ -84,7 +86,7 @@ done
# shellcheck disable=SC2034 # shellcheck disable=SC2034
APP_BASE_NAME=${0##*/} APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD=maximum
@@ -112,7 +114,6 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;; NONSTOP* ) nonstop=true ;;
esac esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM. # Determine the Java command to use to start the JVM.
@@ -170,7 +171,6 @@ fi
# For Cygwin or MSYS, switch paths to Windows format before running java # For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" ) JAVACMD=$( cygpath --unix "$JAVACMD" )
@@ -203,15 +203,14 @@ fi
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command: # Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped. # and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line. # treated as '${Hostname}' itself on the command line.
set -- \ set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \ "-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
org.gradle.wrapper.GradleWrapperMain \
"$@" "$@"
# Stop when "xargs" is not available. # Stop when "xargs" is not available.
+13 -12
View File
@@ -13,6 +13,8 @@
@rem See the License for the specific language governing permissions and @rem See the License for the specific language governing permissions and
@rem limitations under the License. @rem limitations under the License.
@rem @rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off @if "%DEBUG%"=="" @echo off
@rem ########################################################################## @rem ##########################################################################
@@ -43,11 +45,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1 %JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute if %ERRORLEVEL% equ 0 goto execute
echo. echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. echo location of your Java installation. 1>&2
goto fail goto fail
@@ -57,22 +59,21 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute if exist "%JAVA_EXE%" goto execute
echo. echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. echo location of your Java installation. 1>&2
goto fail goto fail
:execute :execute
@rem Setup the command line @rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle @rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end :end
@rem End local scope for the variables with windows NT shell @rem End local scope for the variables with windows NT shell
-2
View File
@@ -1,2 +0,0 @@
rootProject.name='Test Project'
include ':app'
+28
View File
@@ -0,0 +1,28 @@
pluginManagement {
repositories {
google {
content {
includeGroupByRegex("androidx.*")
includeGroupByRegex("com.android.*")
includeGroupByRegex("com.google.*")
}
}
mavenCentral()
}
}
rootProject.name = "Test Project"
include(":app")
dependencyResolutionManagement {
repositories {
google {
content {
includeGroupByRegex("androidx.*")
includeGroupByRegex("com.android.*")
includeGroupByRegex("com.google.*")
}
}
mavenCentral()
}
}
+3 -2
View File
@@ -2,7 +2,7 @@
"compilerOptions": { "compilerOptions": {
/* Basic Options */ /* Basic Options */
// "incremental": true, /* Enable incremental compilation */ // "incremental": true, /* Enable incremental compilation */
"target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ "target": "es2022", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
// "allowJs": true, /* Allow javascript files to be compiled. */ // "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */ // "checkJs": true, /* Report errors in .js files. */
@@ -45,7 +45,8 @@
// "typeRoots": [], /* List of folders to include type definitions from. */ // "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */ // "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
"skipLibCheck": true
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */