mirror of
https://github.com/reactivecircus/android-emulator-runner.git
synced 2026-09-01 10:09:33 +00:00
Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5d6e86df22 | |||
| 38164a29c1 | |||
| 969c9896d3 | |||
| 29b35ed135 | |||
| 001613e856 | |||
| 937ab7eebc | |||
| b530d96654 | |||
| 016d4d097a | |||
| f9bdb6d84e | |||
| b68ca169d6 | |||
| 66283c0319 | |||
| f2bf410054 | |||
| 62e6348453 | |||
| 450c4c9f73 | |||
| 1dcd009011 | |||
| 2548f9f3fc | |||
| 595a4563df | |||
| a173d65200 | |||
| 50d5b10c01 | |||
| 62dbb605bb | |||
| c77bfe7c0b | |||
| ead704cc5a | |||
| e800ffffe6 | |||
| 8c07710601 | |||
| 3a18f50216 | |||
| 1b985f4c7a | |||
| a3dcdb348b | |||
| f0d1ed2dca | |||
| 81f860b51a | |||
| e93b947104 | |||
| 4b0628e9f8 |
+22
-25
@@ -18,42 +18,37 @@ jobs:
|
||||
timeout-minutes: 15
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
api-level: [23, 29]
|
||||
target: [default, google_apis]
|
||||
arch: [x86]
|
||||
exclude:
|
||||
- target: google_apis
|
||||
api-level: 16
|
||||
- target: google_apis
|
||||
api-level: 23
|
||||
- target: google_apis
|
||||
api-level: 29
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
- os: ubuntu-22.04
|
||||
api-level: 23
|
||||
target: default
|
||||
arch: x86
|
||||
- os: ubuntu-24.04
|
||||
api-level: 24
|
||||
target: playstore
|
||||
arch: x86
|
||||
- os: ubuntu-latest
|
||||
api-level: 30
|
||||
target: aosp_atd
|
||||
arch: x86
|
||||
- os: macos-latest
|
||||
- os: macos-15-intel
|
||||
api-level: 31
|
||||
target: google_apis
|
||||
target: default
|
||||
arch: x86_64
|
||||
- os: ubuntu-latest
|
||||
- os: ubuntu-24.04
|
||||
api-level: 34
|
||||
target: aosp_atd
|
||||
arch: x86_64
|
||||
- os: ubuntu-latest
|
||||
api-level: 35
|
||||
target: google_apis
|
||||
arch: x86_64
|
||||
- os: ubuntu-latest
|
||||
api-level: 34-ext10
|
||||
target: android-automotive
|
||||
arch: x86_64
|
||||
system-image-api-level: 34-ext9
|
||||
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: validate gradle wrapper
|
||||
uses: gradle/wrapper-validation-action@v2
|
||||
|
||||
- name: build, test and lint
|
||||
run: |
|
||||
npm install
|
||||
@@ -64,7 +59,7 @@ jobs:
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: 21
|
||||
java-version: 23
|
||||
|
||||
- uses: actions/cache@v4
|
||||
id: avd-cache
|
||||
@@ -73,9 +68,9 @@ jobs:
|
||||
~/.android/avd/*
|
||||
~/.android/adb*
|
||||
~/.android/debug.keystore
|
||||
key: avd-${{ matrix.api-level }}-${{ matrix.os }}-${{ matrix.target }}
|
||||
key: avd-${{ matrix.api-level }}-${{ matrix.os }}-${{ matrix.target }}-${{ matrix.arch }}
|
||||
|
||||
- uses: gradle/actions/setup-gradle@v3
|
||||
- uses: gradle/actions/setup-gradle@v4
|
||||
|
||||
- name: assemble tests
|
||||
working-directory: test-fixture
|
||||
@@ -95,6 +90,7 @@ jobs:
|
||||
api-level: ${{ matrix.api-level }}
|
||||
target: ${{ matrix.target }}
|
||||
arch: ${{ matrix.arch }}
|
||||
system-image-api-level: ${{ matrix.system-image-api-level }}
|
||||
profile: Galaxy Nexus
|
||||
cores: 2
|
||||
sdcard-path-or-size: 100M
|
||||
@@ -112,6 +108,7 @@ jobs:
|
||||
api-level: ${{ matrix.api-level }}
|
||||
target: ${{ matrix.target }}
|
||||
arch: ${{ matrix.arch }}
|
||||
system-image-api-level: ${{ matrix.system-image-api-level }}
|
||||
profile: Galaxy Nexus
|
||||
cores: 2
|
||||
ram-size: 2048M
|
||||
|
||||
@@ -7,16 +7,18 @@ on:
|
||||
required: true
|
||||
default: 'ubuntu-latest'
|
||||
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
|
||||
default: '30'
|
||||
default: '34'
|
||||
system-image-api-level:
|
||||
description: 'API level of the system image - e.g. 34-ext10, 35-ext15'
|
||||
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
|
||||
default: 'default'
|
||||
arch:
|
||||
description: 'CPU architecture of the system image - x86, x86_64 or arm64-v8a'
|
||||
default: 'x86'
|
||||
default: 'x86_64'
|
||||
emulator-options:
|
||||
description: 'command-line options used when launching the emulator'
|
||||
default: '-no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim'
|
||||
@@ -41,9 +43,6 @@ jobs:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: validate gradle wrapper
|
||||
uses: gradle/wrapper-validation-action@v2
|
||||
|
||||
- name: build, test and lint
|
||||
run: |
|
||||
npm install
|
||||
@@ -54,11 +53,9 @@ jobs:
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: 21
|
||||
java-version: 23
|
||||
|
||||
- uses: gradle/actions/setup-gradle@v3
|
||||
with:
|
||||
gradle-home-cache-cleanup: true
|
||||
- uses: gradle/actions/setup-gradle@v4
|
||||
|
||||
- name: enable KVM for linux runners
|
||||
if: runner.os == 'Linux'
|
||||
@@ -73,6 +70,7 @@ jobs:
|
||||
api-level: ${{ github.event.inputs.api-level }}
|
||||
target: ${{ github.event.inputs.target }}
|
||||
arch: ${{ github.event.inputs.arch }}
|
||||
system-image-api-level: ${{ github.event.inputs.system-image-api-level }}
|
||||
profile: Galaxy Nexus
|
||||
emulator-options: ${{ github.event.inputs.emulator-options }}
|
||||
emulator-build: ${{ github.event.inputs.emulator-build }}
|
||||
|
||||
@@ -1,5 +1,36 @@
|
||||
# Change Log
|
||||
|
||||
## Unreleased
|
||||
|
||||
## 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
|
||||
|
||||
* Fix missing AVD directory in `ubuntu-24.04` runner. - [415](https://github.com/ReactiveCircus/android-emulator-runner/pull/415)
|
||||
* Update SDK build tools to `35.0.0`, update SDK command-line tools to `16.0`. - [#356](https://github.com/ReactiveCircus/android-emulator-runner/pull/356)
|
||||
* Stop changing owner of the SDK directory. - [406](https://github.com/ReactiveCircus/android-emulator-runner/pull/406) [#409](https://github.com/ReactiveCircus/android-emulator-runner/pull/409)
|
||||
* Upgrade to latest npm dependencies. - [#401](https://github.com/ReactiveCircus/android-emulator-runner/pull/401)
|
||||
|
||||
## v2.32.0
|
||||
|
||||
* Add `port` parameter for customizing the emulator port to use. - [#383](https://github.com/ReactiveCircus/android-emulator-runner/pull/383)
|
||||
|
||||
|
||||
## v2.31.0
|
||||
|
||||
* Support setting `VanillaIceCream` as `api-level`. - [#378](https://github.com/ReactiveCircus/android-emulator-runner/pull/378)
|
||||
|
||||
@@ -12,7 +12,7 @@ This presents a challenge when running emulators on CI especially when running e
|
||||
|
||||
## 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
|
||||
@@ -90,7 +90,7 @@ jobs:
|
||||
api-level: ${{ matrix.api-level }}
|
||||
target: ${{ matrix.target }}
|
||||
arch: x86_64
|
||||
profile: Nexus 6
|
||||
profile: pixel_7_pro
|
||||
script: ./gradlew connectedCheck
|
||||
```
|
||||
|
||||
@@ -119,9 +119,34 @@ jobs:
|
||||
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@v4
|
||||
|
||||
- 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:
|
||||
|
||||
1. add a `gradle/actions/setup-gradle@v3` step for caching Gradle, more details see [#229](https://github.com/ReactiveCircus/android-emulator-runner/issues/229)
|
||||
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)
|
||||
2. add an `actions/cache@v4` 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`
|
||||
4. add another `reactivecircus/android-emulator-runner@v2` step to run your tests using existing AVD / snapshot - specify `emulator-options` with `no-snapshot-save`
|
||||
@@ -179,10 +204,11 @@ jobs:
|
||||
|
||||
| **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**. |
|
||||
| `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`. |
|
||||
| `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**. |
|
||||
| `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). |
|
||||
| `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). |
|
||||
| `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` |
|
||||
@@ -191,6 +217,7 @@ jobs:
|
||||
| `avd-name` | Optional | `test` | Custom AVD name used for creating the Android Virtual Device. |
|
||||
| `force-avd-creation` | Optional | `true` | Whether to force create the AVD by overwriting an existing AVD with the same name as `avd-name` - `true` or `false`. |
|
||||
| `emulator-boot-timeout` | Optional | `600` | Emulator boot timeout in seconds. If it takes longer to boot, the action would fail - e.g. `300` for 5 minutes. |
|
||||
| `emulator-port` | Optional | `5554` | Emulator port to use. Allows to run this action on multiple workers on a single machine at the same time. This input is available for the script as `EMULATOR_PORT` enviromental variable. This port is automatically used by android device related tasks in gradle |
|
||||
| `emulator-options` | Optional | See below | Command-line options used when launching the emulator (replacing all default options) - e.g. `-no-window -no-snapshot -camera-back emulated`. |
|
||||
| `disable-animations` | Optional | `true` | Whether to disable animations - `true` or `false`. |
|
||||
| `disable-spellchecker` | Optional | `false` | Whether to disable spellchecker - `true` or `false`. |
|
||||
@@ -242,5 +269,8 @@ 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)
|
||||
- [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)
|
||||
- [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)
|
||||
|
||||
If you are using **Android Emulator Runner** and want your project included in the list, please feel free to open a pull request.
|
||||
|
||||
@@ -1,103 +1,5 @@
|
||||
import * as validator 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();
|
||||
});
|
||||
});
|
||||
import { MAX_PORT, MIN_PORT } from '../src/input-validator';
|
||||
|
||||
describe('arch validator tests', () => {
|
||||
it('Throws if arch is unknown', () => {
|
||||
@@ -172,6 +74,33 @@ describe('force-avd-creation validator tests', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('emulator-port validator tests', () => {
|
||||
it('Validates if emulator-port is even and in range', () => {
|
||||
const func = () => {
|
||||
validator.checkPort(5554);
|
||||
};
|
||||
expect(func).not.toThrow();
|
||||
});
|
||||
it('Throws if emulator-port is lower than MIN_PORT', () => {
|
||||
const func = () => {
|
||||
validator.checkPort(MIN_PORT - 2);
|
||||
};
|
||||
expect(func).toThrow();
|
||||
});
|
||||
it('Throws if emulator-port is higher than MAX_PORT', () => {
|
||||
const func = () => {
|
||||
validator.checkPort(MAX_PORT + 2);
|
||||
};
|
||||
expect(func).toThrow();
|
||||
});
|
||||
it('Throws if emulator-port is odd', () => {
|
||||
const func = () => {
|
||||
validator.checkPort(5555);
|
||||
};
|
||||
expect(func).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('disable-animations validator tests', () => {
|
||||
it('Throws if disable-animations is not a boolean', () => {
|
||||
const func = () => {
|
||||
|
||||
+8
-1
@@ -1,6 +1,8 @@
|
||||
inputs:
|
||||
api-level:
|
||||
type: integer
|
||||
type: string
|
||||
system-image-api-level:
|
||||
type: string
|
||||
target:
|
||||
type: enum
|
||||
allowed-values:
|
||||
@@ -13,6 +15,9 @@ inputs:
|
||||
- android-wear-cn
|
||||
- android-tv
|
||||
- google-tv
|
||||
- android-automotive
|
||||
- android-automotive-playstore
|
||||
- android-desktop
|
||||
arch:
|
||||
type: enum
|
||||
allowed-values:
|
||||
@@ -37,6 +42,8 @@ inputs:
|
||||
type: boolean
|
||||
emulator-boot-timeout:
|
||||
type: integer
|
||||
emulator-port:
|
||||
type: integer
|
||||
emulator-options:
|
||||
type: string
|
||||
disable-animations:
|
||||
|
||||
+8
-2
@@ -6,10 +6,13 @@ branding:
|
||||
color: 'green'
|
||||
inputs:
|
||||
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
|
||||
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:
|
||||
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'
|
||||
arch:
|
||||
description: 'CPU architecture of the system image - x86, x86_64 or arm64-v8a'
|
||||
@@ -36,6 +39,9 @@ inputs:
|
||||
emulator-boot-timeout:
|
||||
description: 'Emulator boot timeout in seconds. If it takes longer to boot, the action would fail - e.g. `300` for 5 minutes'
|
||||
default: '600'
|
||||
emulator-port:
|
||||
description: 'Port to run emulator on, allows to run multiple emulators on the same physical machine'
|
||||
default: '5554'
|
||||
emulator-options:
|
||||
description: 'command-line options used when launching the emulator - e.g. `-no-window -no-snapshot -camera-back emulated`'
|
||||
default: '-no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim'
|
||||
|
||||
+57
-31
@@ -32,39 +32,60 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.killEmulator = exports.launchEmulator = void 0;
|
||||
exports.killEmulator = exports.launchEmulator = exports.createAvd = void 0;
|
||||
const exec = __importStar(require("@actions/exec"));
|
||||
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, emulatorOptions, disableAnimations, disableSpellChecker, disableLinuxHardwareAcceleration, enableHardwareKeyboard) {
|
||||
function createAvd(arch, avdName, cores, diskSize, enableHardwareKeyboard, forceAvdCreation, heapSize, profile, ramSize, sdcardPathOrSize, systemImageApiLevel, target) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
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
|
||||
const avdPath = `${process.env.ANDROID_AVD_HOME}/${avdName}.avd`;
|
||||
if (!fs.existsSync(avdPath) || forceAvdCreation) {
|
||||
const profileOption = profile.trim() !== '' ? `--device '${profile}'` : '';
|
||||
const sdcardPathOrSizeOption = sdcardPathOrSize.trim() !== '' ? `--sdcard '${sdcardPathOrSize}'` : '';
|
||||
console.log(`Creating AVD.`);
|
||||
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}"`);
|
||||
yield exec.exec(`sh -c \\"echo no | avdmanager create avd --force -n "${avdName}" --package 'system-images;android-${systemImageApiLevel};${target};${arch}' ${profileOption} ${sdcardPathOrSizeOption}"`);
|
||||
}
|
||||
if (cores) {
|
||||
yield exec.exec(`sh -c \\"printf 'hw.cpu.ncore=${cores}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
|
||||
}
|
||||
if (ramSize) {
|
||||
yield exec.exec(`sh -c \\"printf 'hw.ramSize=${ramSize}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
|
||||
}
|
||||
if (heapSize) {
|
||||
yield exec.exec(`sh -c \\"printf 'hw.heapSize=${heapSize}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
|
||||
}
|
||||
if (enableHardwareKeyboard) {
|
||||
yield exec.exec(`sh -c \\"printf 'hw.keyboard=yes\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
|
||||
}
|
||||
if (diskSize) {
|
||||
yield exec.exec(`sh -c \\"printf 'disk.dataPartition.size=${diskSize}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
|
||||
if (cores || ramSize || heapSize || enableHardwareKeyboard || diskSize) {
|
||||
const configEntries = [];
|
||||
if (cores) {
|
||||
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 (configEntries.length > 0) {
|
||||
const configContent = configEntries.join('\\n') + '\\n';
|
||||
yield exec.exec(`sh -c \\"printf '${configContent}' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini"`);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
console.log(`::endgroup::`);
|
||||
}
|
||||
});
|
||||
}
|
||||
exports.createAvd = createAvd;
|
||||
/**
|
||||
* Launches an existing AVD instance with the specified configurations.
|
||||
*/
|
||||
function launchEmulator(avdName, disableAnimations, disableLinuxHardwareAcceleration, disableSpellChecker, emulatorBootTimeout, emulatorOptions, enableHardwareKeyboard, port) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
try {
|
||||
console.log(`::group::Launch Emulator`);
|
||||
// turn off hardware acceleration on Linux
|
||||
if (process.platform === 'linux' && disableLinuxHardwareAcceleration) {
|
||||
console.log('Disabling Linux hardware acceleration.');
|
||||
@@ -72,7 +93,7 @@ function launchEmulator(apiLevel, target, arch, profile, cores, ramSize, heapSiz
|
||||
}
|
||||
// start emulator
|
||||
console.log('Starting emulator.');
|
||||
yield exec.exec(`sh -c \\"${process.env.ANDROID_HOME}/emulator/emulator -avd "${avdName}" ${emulatorOptions} &"`, [], {
|
||||
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')) {
|
||||
@@ -82,19 +103,19 @@ function launchEmulator(apiLevel, target, arch, profile, cores, ramSize, heapSiz
|
||||
},
|
||||
});
|
||||
// wait for emulator to complete booting
|
||||
yield waitForDevice(emulatorBootTimeout);
|
||||
yield exec.exec(`adb shell input keyevent 82`);
|
||||
yield waitForDevice(port, emulatorBootTimeout);
|
||||
yield adb(port, `shell input keyevent 82`);
|
||||
if (disableAnimations) {
|
||||
console.log('Disabling animations.');
|
||||
yield exec.exec(`adb shell settings put global window_animation_scale 0.0`);
|
||||
yield exec.exec(`adb shell settings put global transition_animation_scale 0.0`);
|
||||
yield exec.exec(`adb shell settings put global animator_duration_scale 0.0`);
|
||||
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 exec.exec(`adb shell settings put secure spell_checker_enabled 0`);
|
||||
yield adb(port, `shell settings put secure spell_checker_enabled 0`);
|
||||
}
|
||||
if (enableHardwareKeyboard) {
|
||||
yield exec.exec(`adb shell settings put secure show_ime_with_hard_keyboard 0`);
|
||||
yield adb(port, `shell settings put secure show_ime_with_hard_keyboard 0`);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
@@ -106,11 +127,11 @@ exports.launchEmulator = launchEmulator;
|
||||
/**
|
||||
* Kills the running emulator on the default port.
|
||||
*/
|
||||
function killEmulator() {
|
||||
function killEmulator(port) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
try {
|
||||
console.log(`::group::Terminate Emulator`);
|
||||
yield exec.exec(`adb -s emulator-5554 emu kill`);
|
||||
yield adb(port, `emu kill`);
|
||||
}
|
||||
catch (error) {
|
||||
console.log(error instanceof Error ? error.message : error);
|
||||
@@ -121,10 +142,15 @@ function killEmulator() {
|
||||
});
|
||||
}
|
||||
exports.killEmulator = killEmulator;
|
||||
function adb(port, command) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
return yield exec.exec(`adb -s emulator-${port} ${command}`);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Wait for emulator to boot.
|
||||
*/
|
||||
function waitForDevice(emulatorBootTimeout) {
|
||||
function waitForDevice(port, emulatorBootTimeout) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
let booted = false;
|
||||
let attempts = 0;
|
||||
@@ -133,7 +159,7 @@ function waitForDevice(emulatorBootTimeout) {
|
||||
while (!booted) {
|
||||
try {
|
||||
let result = '';
|
||||
yield exec.exec(`adb shell getprop sys.boot_completed`, [], {
|
||||
yield exec.exec(`adb -s emulator-${port} shell getprop sys.boot_completed`, [], {
|
||||
listeners: {
|
||||
stdout: (data) => {
|
||||
result += data.toString();
|
||||
|
||||
+21
-19
@@ -1,28 +1,21 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.checkDiskSize = exports.checkEmulatorBuild = exports.checkEnableHardwareKeyboard = exports.checkDisableLinuxHardwareAcceleration = exports.checkDisableSpellchecker = exports.checkDisableAnimations = exports.checkForceAvdCreation = exports.checkChannel = exports.checkArch = exports.checkTarget = exports.checkApiLevel = exports.PREVIEW_API_LEVELS = exports.VALID_CHANNELS = exports.VALID_ARCHS = exports.VALID_TARGETS = exports.MIN_API_LEVEL = void 0;
|
||||
exports.checkDiskSize = exports.checkEmulatorBuild = exports.checkEnableHardwareKeyboard = exports.checkDisableLinuxHardwareAcceleration = exports.checkDisableSpellchecker = exports.checkDisableAnimations = exports.checkPort = exports.checkForceAvdCreation = exports.checkChannel = exports.checkArch = exports.playstoreTargetSubstitution = exports.MAX_PORT = exports.MIN_PORT = exports.VALID_CHANNELS = exports.VALID_ARCHS = exports.MIN_API_LEVEL = void 0;
|
||||
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_CHANNELS = ['stable', 'beta', 'dev', 'canary'];
|
||||
exports.PREVIEW_API_LEVELS = ['Tiramisu', 'UpsideDownCake', 'VanillaIceCream'];
|
||||
function checkApiLevel(apiLevel) {
|
||||
if (exports.PREVIEW_API_LEVELS.some((previewLevel) => apiLevel.startsWith(previewLevel)))
|
||||
return;
|
||||
if (isNaN(Number(apiLevel)) || !Number.isInteger(Number(apiLevel))) {
|
||||
throw new Error(`Unexpected API level: '${apiLevel}'.`);
|
||||
}
|
||||
if (Number(apiLevel) < exports.MIN_API_LEVEL) {
|
||||
throw new Error(`Minimum API level supported is ${exports.MIN_API_LEVEL}.`);
|
||||
}
|
||||
exports.MIN_PORT = 5554;
|
||||
exports.MAX_PORT = 5584;
|
||||
function playstoreTargetSubstitution(target) {
|
||||
// "playstore" is an allowed shorthand for "google_apis_playstore" images
|
||||
// this is idempotent - return same even if run multiple times on same target
|
||||
if (target === 'playstore')
|
||||
return 'google_apis_playstore';
|
||||
if (target === 'playstore_ps16k')
|
||||
return 'google_apis_playstore_ps16k';
|
||||
return target;
|
||||
}
|
||||
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;
|
||||
exports.playstoreTargetSubstitution = playstoreTargetSubstitution;
|
||||
function checkArch(arch) {
|
||||
if (!exports.VALID_ARCHS.includes(arch)) {
|
||||
throw new Error(`Value for input.arch '${arch}' is unknown. Supported options: ${exports.VALID_ARCHS}.`);
|
||||
@@ -41,6 +34,15 @@ function checkForceAvdCreation(forceAvdCreation) {
|
||||
}
|
||||
}
|
||||
exports.checkForceAvdCreation = checkForceAvdCreation;
|
||||
function checkPort(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}`);
|
||||
}
|
||||
if (port % 2 == 1) {
|
||||
throw new Error(`Emulator port has to be even, was ${port}`);
|
||||
}
|
||||
}
|
||||
exports.checkPort = checkPort;
|
||||
function checkDisableAnimations(disableAnimations) {
|
||||
if (!isValidBoolean(disableAnimations)) {
|
||||
throw new Error(`Input for input.disable-animations should be either 'true' or 'false'.`);
|
||||
|
||||
+20
-9
@@ -42,6 +42,7 @@ const channel_id_mapper_1 = require("./channel-id-mapper");
|
||||
const fs_1 = require("fs");
|
||||
function run() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
let port = input_validator_1.MIN_PORT;
|
||||
try {
|
||||
console.log(`::group::Configure emulator`);
|
||||
let linuxSupportKVM = false;
|
||||
@@ -62,12 +63,14 @@ function run() {
|
||||
}
|
||||
// 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}`);
|
||||
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 targetInput = core.getInput('target');
|
||||
const target = targetInput == 'playstore' ? 'google_apis_playstore' : targetInput;
|
||||
(0, input_validator_1.checkTarget)(target);
|
||||
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');
|
||||
@@ -102,6 +105,10 @@ function run() {
|
||||
// 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}`);
|
||||
@@ -174,7 +181,9 @@ function run() {
|
||||
}));
|
||||
console.log(`::endgroup::`);
|
||||
// install SDK
|
||||
yield (0, sdk_installer_1.installAndroidSdk)(apiLevel, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion);
|
||||
yield (0, sdk_installer_1.installAndroidSdk)(apiLevel, systemImageApiLevel, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion);
|
||||
// create AVD
|
||||
yield (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`);
|
||||
@@ -193,7 +202,7 @@ function run() {
|
||||
console.log(`::endgroup::`);
|
||||
}
|
||||
// launch an emulator
|
||||
yield (0, emulator_manager_1.launchEmulator)(apiLevel, target, arch, profile, cores, ramSize, heapSize, sdcardPathOrSize, diskSize, avdName, forceAvdCreation, emulatorBootTimeout, emulatorOptions, disableAnimations, disableSpellchecker, disableLinuxHardwareAcceleration, enableHardwareKeyboard);
|
||||
yield (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
|
||||
@@ -203,18 +212,20 @@ function run() {
|
||||
for (const script of scripts) {
|
||||
// use array form to avoid various quote escaping problems
|
||||
// caused by exec(`sh -c "${script}"`)
|
||||
yield exec.exec('sh', ['-c', script]);
|
||||
yield exec.exec('sh', ['-c', script], {
|
||||
env: Object.assign(Object.assign({}, process.env), { EMULATOR_PORT: `${port}`, ANDROID_SERIAL: `emulator-${port}` }),
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
core.setFailed(error instanceof Error ? error.message : error);
|
||||
}
|
||||
// finally kill the emulator
|
||||
yield (0, emulator_manager_1.killEmulator)();
|
||||
yield (0, emulator_manager_1.killEmulator)(port);
|
||||
}
|
||||
catch (error) {
|
||||
// kill the emulator so the action can exit
|
||||
yield (0, emulator_manager_1.killEmulator)();
|
||||
yield (0, emulator_manager_1.killEmulator)(port);
|
||||
core.setFailed(error instanceof Error ? error.message : error);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -38,23 +38,20 @@ const exec = __importStar(require("@actions/exec"));
|
||||
const io = __importStar(require("@actions/io"));
|
||||
const tc = __importStar(require("@actions/tool-cache"));
|
||||
const fs = __importStar(require("fs"));
|
||||
const BUILD_TOOLS_VERSION = '34.0.0';
|
||||
// SDK command-line tools 11.0
|
||||
const CMDLINE_TOOLS_URL_MAC = 'https://dl.google.com/android/repository/commandlinetools-mac-10406996_latest.zip';
|
||||
const CMDLINE_TOOLS_URL_LINUX = 'https://dl.google.com/android/repository/commandlinetools-linux-10406996_latest.zip';
|
||||
const BUILD_TOOLS_VERSION = '35.0.0';
|
||||
// SDK command-line tools 16.0
|
||||
const CMDLINE_TOOLS_URL_MAC = 'https://dl.google.com/android/repository/commandlinetools-mac-12266719_latest.zip';
|
||||
const CMDLINE_TOOLS_URL_LINUX = 'https://dl.google.com/android/repository/commandlinetools-linux-12266719_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,
|
||||
* and the system image for the chosen API level, CPU arch, and target.
|
||||
*/
|
||||
function installAndroidSdk(apiLevel, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion) {
|
||||
function installAndroidSdk(apiLevel, systemImageApiLevel, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
try {
|
||||
console.log(`::group::Install Android SDK`);
|
||||
const isOnMac = process.platform === 'darwin';
|
||||
const isArm = process.arch === 'arm64';
|
||||
if (!isOnMac) {
|
||||
yield exec.exec(`sh -c \\"sudo chown $USER:$USER ${process.env.ANDROID_HOME} -R`);
|
||||
}
|
||||
const cmdlineToolsPath = `${process.env.ANDROID_HOME}/cmdline-tools`;
|
||||
if (!fs.existsSync(cmdlineToolsPath)) {
|
||||
console.log('Installing new cmdline-tools.');
|
||||
@@ -66,6 +63,7 @@ function installAndroidSdk(apiLevel, target, arch, channelId, emulatorBuild, ndk
|
||||
// add paths for commandline-tools and platform-tools
|
||||
core.addPath(`${cmdlineToolsPath}/latest:${cmdlineToolsPath}/latest/bin:${process.env.ANDROID_HOME}/platform-tools`);
|
||||
// set standard AVD path
|
||||
yield io.mkdirP(`${process.env.HOME}/.android/avd`);
|
||||
core.exportVariable('ANDROID_AVD_HOME', `${process.env.HOME}/.android/avd`);
|
||||
// accept all Android SDK licenses
|
||||
yield exec.exec(`sh -c \\"yes | sdkmanager --licenses > /dev/null"`);
|
||||
@@ -97,7 +95,7 @@ function installAndroidSdk(apiLevel, target, arch, channelId, emulatorBuild, ndk
|
||||
yield io.rmRF('emulator.zip');
|
||||
}
|
||||
console.log('Installing system images.');
|
||||
yield exec.exec(`sh -c \\"sdkmanager --install 'system-images;android-${apiLevel};${target};${arch}' --channel=${channelId} > /dev/null"`);
|
||||
yield exec.exec(`sh -c \\"sdkmanager --install 'system-images;android-${systemImageApiLevel};${target};${arch}' --channel=${channelId} > /dev/null"`);
|
||||
if (ndkVersion) {
|
||||
console.log(`Installing NDK ${ndkVersion}.`);
|
||||
yield exec.exec(`sh -c \\"sdkmanager --install 'ndk;${ndkVersion}' --channel=${channelId} > /dev/null"`);
|
||||
|
||||
Generated
+14
-14
@@ -2105,12 +2105,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/braces": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fill-range": "^7.0.1"
|
||||
"fill-range": "^7.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@@ -3053,9 +3053,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
@@ -7802,12 +7802,12 @@
|
||||
}
|
||||
},
|
||||
"braces": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fill-range": "^7.0.1"
|
||||
"fill-range": "^7.1.1"
|
||||
}
|
||||
},
|
||||
"browserslist": {
|
||||
@@ -8505,9 +8505,9 @@
|
||||
}
|
||||
},
|
||||
"fill-range": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
|
||||
+69
-45
@@ -2,29 +2,24 @@ import * as exec from '@actions/exec';
|
||||
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(
|
||||
apiLevel: string,
|
||||
target: string,
|
||||
export async function createAvd(
|
||||
arch: string,
|
||||
profile: string,
|
||||
cores: string,
|
||||
ramSize: string,
|
||||
heapSize: string,
|
||||
sdcardPathOrSize: string,
|
||||
diskSize: string,
|
||||
avdName: string,
|
||||
cores: string,
|
||||
diskSize: string,
|
||||
enableHardwareKeyboard: boolean,
|
||||
forceAvdCreation: boolean,
|
||||
emulatorBootTimeout: number,
|
||||
emulatorOptions: string,
|
||||
disableAnimations: boolean,
|
||||
disableSpellChecker: boolean,
|
||||
disableLinuxHardwareAcceleration: boolean,
|
||||
enableHardwareKeyboard: boolean
|
||||
heapSize: string,
|
||||
profile: string,
|
||||
ramSize: string,
|
||||
sdcardPathOrSize: string,
|
||||
systemImageApiLevel: string,
|
||||
target: string
|
||||
): Promise<void> {
|
||||
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
|
||||
const avdPath = `${process.env.ANDROID_AVD_HOME}/${avdName}.avd`;
|
||||
if (!fs.existsSync(avdPath) || forceAvdCreation) {
|
||||
@@ -32,29 +27,54 @@ export async function launchEmulator(
|
||||
const sdcardPathOrSizeOption = sdcardPathOrSize.trim() !== '' ? `--sdcard '${sdcardPathOrSize}'` : '';
|
||||
console.log(`Creating AVD.`);
|
||||
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) {
|
||||
await exec.exec(`sh -c \\"printf 'hw.cpu.ncore=${cores}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
|
||||
}
|
||||
if (cores || ramSize || heapSize || enableHardwareKeyboard || diskSize) {
|
||||
const configEntries: string[] = [];
|
||||
|
||||
if (ramSize) {
|
||||
await exec.exec(`sh -c \\"printf 'hw.ramSize=${ramSize}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
|
||||
}
|
||||
if (cores) {
|
||||
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) {
|
||||
await exec.exec(`sh -c \\"printf 'hw.heapSize=${heapSize}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
|
||||
if (configEntries.length > 0) {
|
||||
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`);
|
||||
}
|
||||
|
||||
if (diskSize) {
|
||||
await exec.exec(`sh -c \\"printf 'disk.dataPartition.size=${diskSize}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
|
||||
}
|
||||
/**
|
||||
* Launches an existing AVD instance with the specified configurations.
|
||||
*/
|
||||
export async function launchEmulator(
|
||||
avdName: string,
|
||||
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
|
||||
if (process.platform === 'linux' && disableLinuxHardwareAcceleration) {
|
||||
@@ -65,7 +85,7 @@ export async function launchEmulator(
|
||||
// start emulator
|
||||
console.log('Starting emulator.');
|
||||
|
||||
await exec.exec(`sh -c \\"${process.env.ANDROID_HOME}/emulator/emulator -avd "${avdName}" ${emulatorOptions} &"`, [], {
|
||||
await exec.exec(`sh -c \\"${process.env.ANDROID_HOME}/emulator/emulator -port ${port} -avd "${avdName}" ${emulatorOptions} &"`, [], {
|
||||
listeners: {
|
||||
stderr: (data: Buffer) => {
|
||||
if (data.toString().includes('invalid command-line parameter')) {
|
||||
@@ -76,20 +96,20 @@ export async function launchEmulator(
|
||||
});
|
||||
|
||||
// wait for emulator to complete booting
|
||||
await waitForDevice(emulatorBootTimeout);
|
||||
await exec.exec(`adb shell input keyevent 82`);
|
||||
await waitForDevice(port, emulatorBootTimeout);
|
||||
await adb(port, `shell input keyevent 82`);
|
||||
|
||||
if (disableAnimations) {
|
||||
console.log('Disabling animations.');
|
||||
await exec.exec(`adb shell settings put global window_animation_scale 0.0`);
|
||||
await exec.exec(`adb shell settings put global transition_animation_scale 0.0`);
|
||||
await exec.exec(`adb shell settings put global animator_duration_scale 0.0`);
|
||||
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 exec.exec(`adb shell settings put secure spell_checker_enabled 0`);
|
||||
await adb(port, `shell settings put secure spell_checker_enabled 0`);
|
||||
}
|
||||
if (enableHardwareKeyboard) {
|
||||
await exec.exec(`adb shell settings put secure show_ime_with_hard_keyboard 0`);
|
||||
await adb(port, `shell settings put secure show_ime_with_hard_keyboard 0`);
|
||||
}
|
||||
} finally {
|
||||
console.log(`::endgroup::`);
|
||||
@@ -99,10 +119,10 @@ export async function launchEmulator(
|
||||
/**
|
||||
* Kills the running emulator on the default port.
|
||||
*/
|
||||
export async function killEmulator(): Promise<void> {
|
||||
export async function killEmulator(port: number): Promise<void> {
|
||||
try {
|
||||
console.log(`::group::Terminate Emulator`);
|
||||
await exec.exec(`adb -s emulator-5554 emu kill`);
|
||||
await adb(port, `emu kill`);
|
||||
} catch (error) {
|
||||
console.log(error instanceof Error ? error.message : error);
|
||||
} finally {
|
||||
@@ -110,10 +130,14 @@ export async function killEmulator(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
async function adb(port: number, command: string): Promise<number> {
|
||||
return await exec.exec(`adb -s emulator-${port} ${command}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for emulator to boot.
|
||||
*/
|
||||
async function waitForDevice(emulatorBootTimeout: number): Promise<void> {
|
||||
async function waitForDevice(port: number, emulatorBootTimeout: number): Promise<void> {
|
||||
let booted = false;
|
||||
let attempts = 0;
|
||||
const retryInterval = 2; // retry every 2 seconds
|
||||
@@ -121,7 +145,7 @@ async function waitForDevice(emulatorBootTimeout: number): Promise<void> {
|
||||
while (!booted) {
|
||||
try {
|
||||
let result = '';
|
||||
await exec.exec(`adb shell getprop sys.boot_completed`, [], {
|
||||
await exec.exec(`adb -s emulator-${port} shell getprop sys.boot_completed`, [], {
|
||||
listeners: {
|
||||
stdout: (data: Buffer) => {
|
||||
result += data.toString();
|
||||
|
||||
+17
-16
@@ -1,23 +1,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_CHANNELS: Array<string> = ['stable', 'beta', 'dev', 'canary'];
|
||||
export const PREVIEW_API_LEVELS: Array<string> = ['Tiramisu', 'UpsideDownCake', 'VanillaIceCream'];
|
||||
export const MIN_PORT = 5554;
|
||||
export const MAX_PORT = 5584;
|
||||
|
||||
export function checkApiLevel(apiLevel: string): void {
|
||||
if (PREVIEW_API_LEVELS.some((previewLevel) => apiLevel.startsWith(previewLevel))) return;
|
||||
if (isNaN(Number(apiLevel)) || !Number.isInteger(Number(apiLevel))) {
|
||||
throw new Error(`Unexpected API level: '${apiLevel}'.`);
|
||||
}
|
||||
if (Number(apiLevel) < MIN_API_LEVEL) {
|
||||
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 playstoreTargetSubstitution(target: string): string {
|
||||
// "playstore" is an allowed shorthand for "google_apis_playstore" images
|
||||
// this is idempotent - return same even if run multiple times on same target
|
||||
if (target === 'playstore') return 'google_apis_playstore';
|
||||
if (target === 'playstore_ps16k') return 'google_apis_playstore_ps16k';
|
||||
return target;
|
||||
}
|
||||
|
||||
export function checkArch(arch: string): void {
|
||||
@@ -38,6 +30,15 @@ export function checkForceAvdCreation(forceAvdCreation: string): void {
|
||||
}
|
||||
}
|
||||
|
||||
export function checkPort(port: number): void {
|
||||
if (port < MIN_PORT || port > MAX_PORT) {
|
||||
throw new Error(`Emulator port is outside of the supported port range [${MIN_PORT}, ${MAX_PORT}], was ${port}`);
|
||||
}
|
||||
if (port % 2 == 1) {
|
||||
throw new Error(`Emulator port has to be even, was ${port}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function checkDisableAnimations(disableAnimations: string): void {
|
||||
if (!isValidBoolean(disableAnimations)) {
|
||||
throw new Error(`Input for input.disable-animations should be either 'true' or 'false'.`);
|
||||
|
||||
+27
-30
@@ -1,8 +1,6 @@
|
||||
import * as core from '@actions/core';
|
||||
import { installAndroidSdk } from './sdk-installer';
|
||||
import {
|
||||
checkApiLevel,
|
||||
checkTarget,
|
||||
checkArch,
|
||||
checkDisableAnimations,
|
||||
checkEmulatorBuild,
|
||||
@@ -12,14 +10,18 @@ import {
|
||||
checkChannel,
|
||||
checkEnableHardwareKeyboard,
|
||||
checkDiskSize,
|
||||
checkPort,
|
||||
playstoreTargetSubstitution,
|
||||
MIN_PORT,
|
||||
} from './input-validator';
|
||||
import { launchEmulator, killEmulator } from './emulator-manager';
|
||||
import { createAvd, launchEmulator, killEmulator } from './emulator-manager';
|
||||
import * as exec from '@actions/exec';
|
||||
import { parseScript } from './script-parser';
|
||||
import { getChannelId } from './channel-id-mapper';
|
||||
import { accessSync, constants } from 'fs';
|
||||
|
||||
async function run() {
|
||||
let port: number = MIN_PORT;
|
||||
try {
|
||||
console.log(`::group::Configure emulator`);
|
||||
let linuxSupportKVM = false;
|
||||
@@ -41,13 +43,16 @@ async function run() {
|
||||
|
||||
// API level of the platform and system image
|
||||
const apiLevel = core.getInput('api-level', { required: true });
|
||||
checkApiLevel(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
|
||||
const targetInput = core.getInput('target');
|
||||
const target = targetInput == 'playstore' ? 'google_apis_playstore' : targetInput;
|
||||
checkTarget(target);
|
||||
const target = playstoreTargetSubstitution(core.getInput('target'));
|
||||
console.log(`target: ${target}`);
|
||||
|
||||
// CPU architecture of the system image
|
||||
@@ -93,6 +98,11 @@ async function run() {
|
||||
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);
|
||||
checkPort(port);
|
||||
console.log(`emulator port: ${port}`);
|
||||
|
||||
// emulator options
|
||||
const emulatorOptions = core.getInput('emulator-options').trim();
|
||||
console.log(`emulator options: ${emulatorOptions}`);
|
||||
@@ -177,7 +187,10 @@ async function run() {
|
||||
console.log(`::endgroup::`);
|
||||
|
||||
// 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
|
||||
if (preEmulatorLaunchScripts !== undefined) {
|
||||
@@ -197,25 +210,7 @@ async function run() {
|
||||
}
|
||||
|
||||
// launch an emulator
|
||||
await launchEmulator(
|
||||
apiLevel,
|
||||
target,
|
||||
arch,
|
||||
profile,
|
||||
cores,
|
||||
ramSize,
|
||||
heapSize,
|
||||
sdcardPathOrSize,
|
||||
diskSize,
|
||||
avdName,
|
||||
forceAvdCreation,
|
||||
emulatorBootTimeout,
|
||||
emulatorOptions,
|
||||
disableAnimations,
|
||||
disableSpellchecker,
|
||||
disableLinuxHardwareAcceleration,
|
||||
enableHardwareKeyboard
|
||||
);
|
||||
await launchEmulator(avdName, disableAnimations, disableLinuxHardwareAcceleration, disableSpellchecker, emulatorBootTimeout, emulatorOptions, enableHardwareKeyboard, port);
|
||||
|
||||
// execute the custom script
|
||||
try {
|
||||
@@ -226,17 +221,19 @@ async function run() {
|
||||
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]);
|
||||
await exec.exec('sh', ['-c', script], {
|
||||
env: { ...process.env, EMULATOR_PORT: `${port}`, ANDROID_SERIAL: `emulator-${port}` },
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
core.setFailed(error instanceof Error ? error.message : (error as string));
|
||||
}
|
||||
|
||||
// finally kill the emulator
|
||||
await killEmulator();
|
||||
await killEmulator(port);
|
||||
} catch (error) {
|
||||
// kill the emulator so the action can exit
|
||||
await killEmulator();
|
||||
await killEmulator(port);
|
||||
core.setFailed(error instanceof Error ? error.message : (error as string));
|
||||
}
|
||||
}
|
||||
|
||||
+16
-10
@@ -4,25 +4,30 @@ import * as io from '@actions/io';
|
||||
import * as tc from '@actions/tool-cache';
|
||||
import * as fs from 'fs';
|
||||
|
||||
const BUILD_TOOLS_VERSION = '34.0.0';
|
||||
// SDK command-line tools 11.0
|
||||
const CMDLINE_TOOLS_URL_MAC = 'https://dl.google.com/android/repository/commandlinetools-mac-10406996_latest.zip';
|
||||
const CMDLINE_TOOLS_URL_LINUX = 'https://dl.google.com/android/repository/commandlinetools-linux-10406996_latest.zip';
|
||||
const BUILD_TOOLS_VERSION = '35.0.0';
|
||||
// SDK command-line tools 16.0
|
||||
const CMDLINE_TOOLS_URL_MAC = 'https://dl.google.com/android/repository/commandlinetools-mac-12266719_latest.zip';
|
||||
const CMDLINE_TOOLS_URL_LINUX = 'https://dl.google.com/android/repository/commandlinetools-linux-12266719_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,
|
||||
* 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 {
|
||||
console.log(`::group::Install Android SDK`);
|
||||
const isOnMac = process.platform === 'darwin';
|
||||
const isArm = process.arch === 'arm64';
|
||||
|
||||
if (!isOnMac) {
|
||||
await exec.exec(`sh -c \\"sudo chown $USER:$USER ${process.env.ANDROID_HOME} -R`);
|
||||
}
|
||||
|
||||
const cmdlineToolsPath = `${process.env.ANDROID_HOME}/cmdline-tools`;
|
||||
if (!fs.existsSync(cmdlineToolsPath)) {
|
||||
console.log('Installing new cmdline-tools.');
|
||||
@@ -36,6 +41,7 @@ export async function installAndroidSdk(apiLevel: string, target: string, arch:
|
||||
core.addPath(`${cmdlineToolsPath}/latest:${cmdlineToolsPath}/latest/bin:${process.env.ANDROID_HOME}/platform-tools`);
|
||||
|
||||
// set standard AVD path
|
||||
await io.mkdirP(`${process.env.HOME}/.android/avd`);
|
||||
core.exportVariable('ANDROID_AVD_HOME', `${process.env.HOME}/.android/avd`);
|
||||
|
||||
// accept all Android SDK licenses
|
||||
@@ -69,7 +75,7 @@ export async function installAndroidSdk(apiLevel: string, target: string, arch:
|
||||
await io.rmRF('emulator.zip');
|
||||
}
|
||||
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) {
|
||||
console.log(`Installing NDK ${ndkVersion}.`);
|
||||
|
||||
@@ -4,13 +4,13 @@ apply plugin: 'kotlin-android'
|
||||
android {
|
||||
namespace = "com.example.testapp"
|
||||
|
||||
compileSdkVersion 34
|
||||
buildToolsVersion "34.0.0"
|
||||
compileSdkVersion 35
|
||||
buildToolsVersion "35.0.0"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.example.testapp"
|
||||
minSdkVersion 15
|
||||
targetSdkVersion 34
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 35
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
@@ -30,8 +30,7 @@ tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
||||
implementation 'androidx.appcompat:appcompat:1.7.0'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ buildscript {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.2.0'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.21"
|
||||
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
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
Reference in New Issue
Block a user