mirror of
https://github.com/reactivecircus/android-emulator-runner.git
synced 2026-09-03 19:09:32 +00:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 62dbb605bb | |||
| c77bfe7c0b | |||
| ead704cc5a | |||
| e800ffffe6 | |||
| 8c07710601 | |||
| 3a18f50216 | |||
| 1b985f4c7a | |||
| a3dcdb348b | |||
| f0d1ed2dca | |||
| 81f860b51a | |||
| e93b947104 | |||
| 4b0628e9f8 | |||
| 77986be265 | |||
| ca77a1081f | |||
| 1fef06a960 | |||
| d8da5b6410 | |||
| a0c0e7216b | |||
| 9d5d6236f4 |
@@ -9,5 +9,5 @@ jobs:
|
|||||||
validate-typings:
|
validate-typings:
|
||||||
runs-on: "ubuntu-latest"
|
runs-on: "ubuntu-latest"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: krzema12/github-actions-typing@v0
|
- uses: krzema12/github-actions-typing@v0
|
||||||
|
|||||||
+21
-31
@@ -18,41 +18,31 @@ jobs:
|
|||||||
timeout-minutes: 15
|
timeout-minutes: 15
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
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:
|
include:
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-22.04
|
||||||
|
api-level: 23
|
||||||
|
target: default
|
||||||
|
arch: x86
|
||||||
|
- os: ubuntu-24.04
|
||||||
api-level: 24
|
api-level: 24
|
||||||
target: playstore
|
target: playstore
|
||||||
arch: x86
|
arch: x86
|
||||||
- os: ubuntu-latest
|
- os: macos-13
|
||||||
api-level: 30
|
|
||||||
target: aosp_atd
|
|
||||||
arch: x86
|
|
||||||
- os: macos-latest
|
|
||||||
api-level: 31
|
api-level: 31
|
||||||
target: google_apis
|
target: default
|
||||||
arch: x86_64
|
arch: x86_64
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-24.04
|
||||||
api-level: 34
|
api-level: 34
|
||||||
target: aosp_atd
|
target: aosp_atd
|
||||||
arch: x86_64
|
arch: x86_64
|
||||||
|
- os: ubuntu-lastest
|
||||||
|
api-level: 35
|
||||||
|
target: google_apis
|
||||||
|
arch: x86_64
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: checkout
|
- name: checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: validate gradle wrapper
|
|
||||||
uses: gradle/wrapper-validation-action@v1
|
|
||||||
|
|
||||||
- name: build, test and lint
|
- name: build, test and lint
|
||||||
run: |
|
run: |
|
||||||
@@ -61,25 +51,25 @@ jobs:
|
|||||||
npm run lint
|
npm run lint
|
||||||
npm test
|
npm test
|
||||||
|
|
||||||
- uses: actions/setup-java@v3
|
- uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
distribution: 'zulu'
|
distribution: 'zulu'
|
||||||
java-version: 21
|
java-version: 23
|
||||||
|
|
||||||
- uses: actions/cache@v3
|
- uses: actions/cache@v4
|
||||||
id: avd-cache
|
id: avd-cache
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.android/avd/*
|
~/.android/avd/*
|
||||||
~/.android/adb*
|
~/.android/adb*
|
||||||
~/.android/debug.keystore
|
~/.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@v4
|
||||||
|
|
||||||
- name: assemble tests
|
- name: assemble tests
|
||||||
uses: gradle/gradle-build-action@v2
|
working-directory: test-fixture
|
||||||
with:
|
run: ./gradlew assembleAndroidTest
|
||||||
build-root-directory: test-fixture
|
|
||||||
arguments: assembleAndroidTest
|
|
||||||
|
|
||||||
- name: enable KVM for linux runners
|
- name: enable KVM for linux runners
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
|
|||||||
@@ -9,14 +9,14 @@ on:
|
|||||||
api-level:
|
api-level:
|
||||||
description: 'API level of the platform and system image'
|
description: 'API level of the platform and system image'
|
||||||
required: true
|
required: true
|
||||||
default: '30'
|
default: '34'
|
||||||
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 or google-tv'
|
||||||
required: true
|
required: true
|
||||||
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'
|
||||||
default: 'x86'
|
default: 'x86_64'
|
||||||
emulator-options:
|
emulator-options:
|
||||||
description: 'command-line options used when launching the emulator'
|
description: 'command-line options used when launching the emulator'
|
||||||
default: '-no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim'
|
default: '-no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim'
|
||||||
@@ -39,10 +39,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: checkout
|
- name: checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: validate gradle wrapper
|
|
||||||
uses: gradle/wrapper-validation-action@v1
|
|
||||||
|
|
||||||
- name: build, test and lint
|
- name: build, test and lint
|
||||||
run: |
|
run: |
|
||||||
@@ -51,14 +48,12 @@ jobs:
|
|||||||
npm run lint
|
npm run lint
|
||||||
npm test
|
npm test
|
||||||
|
|
||||||
- uses: actions/setup-java@v3
|
- uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
distribution: 'zulu'
|
distribution: 'zulu'
|
||||||
java-version: 21
|
java-version: 23
|
||||||
|
|
||||||
- uses: gradle/gradle-build-action@v2
|
- uses: gradle/actions/setup-gradle@v4
|
||||||
with:
|
|
||||||
gradle-home-cache-cleanup: true
|
|
||||||
|
|
||||||
- name: enable KVM for linux runners
|
- name: enable KVM for linux runners
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
|
|||||||
@@ -1,5 +1,22 @@
|
|||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
|
## 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)
|
||||||
|
* Install `platforms` for the specified `api-level`. - [#384](https://github.com/ReactiveCircus/android-emulator-runner/pull/384)
|
||||||
|
|
||||||
## v2.30.1
|
## v2.30.1
|
||||||
|
|
||||||
* Run action on Node 20. - [#371](https://github.com/ReactiveCircus/android-emulator-runner/pull/371)
|
* Run action on Node 20. - [#371](https://github.com/ReactiveCircus/android-emulator-runner/pull/371)
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: checkout
|
- name: checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- 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@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Enable KVM
|
- name: Enable KVM
|
||||||
run: |
|
run: |
|
||||||
@@ -102,7 +102,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: checkout
|
- name: checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Enable KVM
|
- name: Enable KVM
|
||||||
run: |
|
run: |
|
||||||
@@ -121,8 +121,8 @@ jobs:
|
|||||||
|
|
||||||
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/gradle-build-action@v2` 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@v3` step for caching the `avd`
|
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`
|
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 +135,7 @@ jobs:
|
|||||||
api-level: [21, 23, 29]
|
api-level: [21, 23, 29]
|
||||||
steps:
|
steps:
|
||||||
- name: checkout
|
- name: checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Enable KVM
|
- name: Enable KVM
|
||||||
run: |
|
run: |
|
||||||
@@ -144,10 +144,10 @@ jobs:
|
|||||||
sudo udevadm trigger --name-match=kvm
|
sudo udevadm trigger --name-match=kvm
|
||||||
|
|
||||||
- name: Gradle cache
|
- name: Gradle cache
|
||||||
uses: gradle/gradle-build-action@v2
|
uses: gradle/actions/setup-gradle@v3
|
||||||
|
|
||||||
- name: AVD cache
|
- name: AVD cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
id: avd-cache
|
id: avd-cache
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
@@ -191,6 +191,7 @@ jobs:
|
|||||||
| `avd-name` | Optional | `test` | Custom AVD name used for creating the Android Virtual Device. |
|
| `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`. |
|
| `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-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`. |
|
| `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-animations` | Optional | `true` | Whether to disable animations - `true` or `false`. |
|
||||||
| `disable-spellchecker` | Optional | `false` | Whether to disable spellchecker - `true` or `false`. |
|
| `disable-spellchecker` | Optional | `false` | Whether to disable spellchecker - `true` or `false`. |
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import * as validator from '../src/input-validator';
|
import * as validator from '../src/input-validator';
|
||||||
|
import { MAX_PORT, MIN_PORT } from '../src/input-validator';
|
||||||
|
|
||||||
describe('api-level validator tests', () => {
|
describe('api-level validator tests', () => {
|
||||||
it('Throws if api-level is not a number', () => {
|
it('Throws if api-level is not a number', () => {
|
||||||
@@ -172,6 +173,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', () => {
|
describe('disable-animations validator tests', () => {
|
||||||
it('Throws if disable-animations is not a boolean', () => {
|
it('Throws if disable-animations is not a boolean', () => {
|
||||||
const func = () => {
|
const func = () => {
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ inputs:
|
|||||||
type: boolean
|
type: boolean
|
||||||
emulator-boot-timeout:
|
emulator-boot-timeout:
|
||||||
type: integer
|
type: integer
|
||||||
|
emulator-port:
|
||||||
|
type: integer
|
||||||
emulator-options:
|
emulator-options:
|
||||||
type: string
|
type: string
|
||||||
disable-animations:
|
disable-animations:
|
||||||
|
|||||||
@@ -36,6 +36,9 @@ inputs:
|
|||||||
emulator-boot-timeout:
|
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'
|
description: 'Emulator boot timeout in seconds. If it takes longer to boot, the action would fail - e.g. `300` for 5 minutes'
|
||||||
default: '600'
|
default: '600'
|
||||||
|
emulator-port:
|
||||||
|
description: 'Port to run emulator on, allows to run multiple emulators on the same physical machine'
|
||||||
|
default: '5554'
|
||||||
emulator-options:
|
emulator-options:
|
||||||
description: 'command-line options used when launching the emulator - e.g. `-no-window -no-snapshot -camera-back emulated`'
|
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'
|
default: '-no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim'
|
||||||
|
|||||||
+18
-13
@@ -38,7 +38,7 @@ const fs = __importStar(require("fs"));
|
|||||||
/**
|
/**
|
||||||
* Creates and launches a new AVD instance with the specified configurations.
|
* Creates and launches 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 launchEmulator(apiLevel, target, arch, profile, cores, ramSize, heapSize, sdcardPathOrSize, diskSize, avdName, forceAvdCreation, emulatorBootTimeout, port, emulatorOptions, disableAnimations, disableSpellChecker, disableLinuxHardwareAcceleration, enableHardwareKeyboard) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
try {
|
try {
|
||||||
console.log(`::group::Launch Emulator`);
|
console.log(`::group::Launch Emulator`);
|
||||||
@@ -72,7 +72,7 @@ function launchEmulator(apiLevel, target, arch, profile, cores, ramSize, heapSiz
|
|||||||
}
|
}
|
||||||
// start emulator
|
// start emulator
|
||||||
console.log('Starting 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: {
|
listeners: {
|
||||||
stderr: (data) => {
|
stderr: (data) => {
|
||||||
if (data.toString().includes('invalid command-line parameter')) {
|
if (data.toString().includes('invalid command-line parameter')) {
|
||||||
@@ -82,19 +82,19 @@ function launchEmulator(apiLevel, target, arch, profile, cores, ramSize, heapSiz
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
// wait for emulator to complete booting
|
// wait for emulator to complete booting
|
||||||
yield waitForDevice(emulatorBootTimeout);
|
yield waitForDevice(port, emulatorBootTimeout);
|
||||||
yield exec.exec(`adb shell input keyevent 82`);
|
yield adb(port, `shell input keyevent 82`);
|
||||||
if (disableAnimations) {
|
if (disableAnimations) {
|
||||||
console.log('Disabling animations.');
|
console.log('Disabling animations.');
|
||||||
yield exec.exec(`adb shell settings put global window_animation_scale 0.0`);
|
yield adb(port, `shell settings put global window_animation_scale 0.0`);
|
||||||
yield exec.exec(`adb shell settings put global transition_animation_scale 0.0`);
|
yield adb(port, `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 animator_duration_scale 0.0`);
|
||||||
}
|
}
|
||||||
if (disableSpellChecker) {
|
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) {
|
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 {
|
finally {
|
||||||
@@ -106,11 +106,11 @@ exports.launchEmulator = launchEmulator;
|
|||||||
/**
|
/**
|
||||||
* Kills the running emulator on the default port.
|
* Kills the running emulator on the default port.
|
||||||
*/
|
*/
|
||||||
function killEmulator() {
|
function killEmulator(port) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
try {
|
try {
|
||||||
console.log(`::group::Terminate Emulator`);
|
console.log(`::group::Terminate Emulator`);
|
||||||
yield exec.exec(`adb -s emulator-5554 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);
|
||||||
@@ -121,10 +121,15 @@ function killEmulator() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
exports.killEmulator = 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.
|
* Wait for emulator to boot.
|
||||||
*/
|
*/
|
||||||
function waitForDevice(emulatorBootTimeout) {
|
function waitForDevice(port, emulatorBootTimeout) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
let booted = false;
|
let booted = false;
|
||||||
let attempts = 0;
|
let attempts = 0;
|
||||||
@@ -133,7 +138,7 @@ function waitForDevice(emulatorBootTimeout) {
|
|||||||
while (!booted) {
|
while (!booted) {
|
||||||
try {
|
try {
|
||||||
let result = '';
|
let result = '';
|
||||||
yield exec.exec(`adb 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();
|
||||||
|
|||||||
+14
-2
@@ -1,12 +1,15 @@
|
|||||||
"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.checkForceAvdCreation = exports.checkChannel = exports.checkArch = exports.checkTarget = exports.checkApiLevel = 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.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.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_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.MAX_PORT = 5584;
|
||||||
|
exports.PREVIEW_API_LEVELS = ['Tiramisu', 'UpsideDownCake', 'VanillaIceCream'];
|
||||||
function checkApiLevel(apiLevel) {
|
function checkApiLevel(apiLevel) {
|
||||||
if (apiLevel.startsWith('UpsideDownCake') || apiLevel === 'TiramisuPrivacySandbox')
|
if (exports.PREVIEW_API_LEVELS.some((previewLevel) => apiLevel.startsWith(previewLevel)))
|
||||||
return;
|
return;
|
||||||
if (isNaN(Number(apiLevel)) || !Number.isInteger(Number(apiLevel))) {
|
if (isNaN(Number(apiLevel)) || !Number.isInteger(Number(apiLevel))) {
|
||||||
throw new Error(`Unexpected API level: '${apiLevel}'.`);
|
throw new Error(`Unexpected API level: '${apiLevel}'.`);
|
||||||
@@ -40,6 +43,15 @@ function checkForceAvdCreation(forceAvdCreation) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.checkForceAvdCreation = checkForceAvdCreation;
|
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) {
|
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'.`);
|
||||||
|
|||||||
+11
-4
@@ -42,6 +42,7 @@ const channel_id_mapper_1 = require("./channel-id-mapper");
|
|||||||
const fs_1 = require("fs");
|
const fs_1 = require("fs");
|
||||||
function run() {
|
function run() {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
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;
|
||||||
@@ -102,6 +103,10 @@ function run() {
|
|||||||
// Emulator boot timeout seconds
|
// Emulator boot timeout seconds
|
||||||
const emulatorBootTimeout = parseInt(core.getInput('emulator-boot-timeout'), 10);
|
const emulatorBootTimeout = parseInt(core.getInput('emulator-boot-timeout'), 10);
|
||||||
console.log(`Emulator boot timeout: ${emulatorBootTimeout}`);
|
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
|
// emulator options
|
||||||
const emulatorOptions = core.getInput('emulator-options').trim();
|
const emulatorOptions = core.getInput('emulator-options').trim();
|
||||||
console.log(`emulator options: ${emulatorOptions}`);
|
console.log(`emulator options: ${emulatorOptions}`);
|
||||||
@@ -193,7 +198,7 @@ function run() {
|
|||||||
console.log(`::endgroup::`);
|
console.log(`::endgroup::`);
|
||||||
}
|
}
|
||||||
// launch an emulator
|
// 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)(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 {
|
||||||
// move to custom working directory if set
|
// move to custom working directory if set
|
||||||
@@ -203,18 +208,20 @@ function run() {
|
|||||||
for (const script of scripts) {
|
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 "${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) {
|
catch (error) {
|
||||||
core.setFailed(error instanceof Error ? error.message : error);
|
core.setFailed(error instanceof Error ? error.message : error);
|
||||||
}
|
}
|
||||||
// finally kill the emulator
|
// finally kill the emulator
|
||||||
yield (0, emulator_manager_1.killEmulator)();
|
yield (0, emulator_manager_1.killEmulator)(port);
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
// kill the emulator so the action can exit
|
// 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);
|
core.setFailed(error instanceof Error ? error.message : error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -38,10 +38,10 @@ 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 = '34.0.0';
|
const BUILD_TOOLS_VERSION = '35.0.0';
|
||||||
// SDK command-line tools 11.0
|
// SDK command-line tools 16.0
|
||||||
const CMDLINE_TOOLS_URL_MAC = 'https://dl.google.com/android/repository/commandlinetools-mac-10406996_latest.zip';
|
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-10406996_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,
|
* 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.
|
||||||
@@ -52,9 +52,6 @@ function installAndroidSdk(apiLevel, target, arch, channelId, emulatorBuild, ndk
|
|||||||
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';
|
||||||
if (!isOnMac) {
|
|
||||||
yield exec.exec(`sh -c \\"sudo chown $USER:$USER ${process.env.ANDROID_HOME} -R`);
|
|
||||||
}
|
|
||||||
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.');
|
||||||
@@ -66,11 +63,12 @@ function installAndroidSdk(apiLevel, target, arch, channelId, emulatorBuild, ndk
|
|||||||
// 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
|
||||||
|
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
|
||||||
yield 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.');
|
||||||
yield exec.exec(`sh -c \\"sdkmanager --install 'build-tools;${BUILD_TOOLS_VERSION}' platform-tools > /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.');
|
||||||
yield 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) {
|
||||||
|
|||||||
Generated
+14
-14
@@ -2105,12 +2105,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/braces": {
|
"node_modules/braces": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fill-range": "^7.0.1"
|
"fill-range": "^7.1.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
@@ -3053,9 +3053,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/fill-range": {
|
"node_modules/fill-range": {
|
||||||
"version": "7.0.1",
|
"version": "7.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"to-regex-range": "^5.0.1"
|
"to-regex-range": "^5.0.1"
|
||||||
@@ -7802,12 +7802,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"braces": {
|
"braces": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"fill-range": "^7.0.1"
|
"fill-range": "^7.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"browserslist": {
|
"browserslist": {
|
||||||
@@ -8505,9 +8505,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fill-range": {
|
"fill-range": {
|
||||||
"version": "7.0.1",
|
"version": "7.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"to-regex-range": "^5.0.1"
|
"to-regex-range": "^5.0.1"
|
||||||
|
|||||||
+17
-12
@@ -17,6 +17,7 @@ export async function launchEmulator(
|
|||||||
avdName: string,
|
avdName: string,
|
||||||
forceAvdCreation: boolean,
|
forceAvdCreation: boolean,
|
||||||
emulatorBootTimeout: number,
|
emulatorBootTimeout: number,
|
||||||
|
port: number,
|
||||||
emulatorOptions: string,
|
emulatorOptions: string,
|
||||||
disableAnimations: boolean,
|
disableAnimations: boolean,
|
||||||
disableSpellChecker: boolean,
|
disableSpellChecker: boolean,
|
||||||
@@ -65,7 +66,7 @@ export async function launchEmulator(
|
|||||||
// start emulator
|
// start emulator
|
||||||
console.log('Starting 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: {
|
listeners: {
|
||||||
stderr: (data: Buffer) => {
|
stderr: (data: Buffer) => {
|
||||||
if (data.toString().includes('invalid command-line parameter')) {
|
if (data.toString().includes('invalid command-line parameter')) {
|
||||||
@@ -76,20 +77,20 @@ export async function launchEmulator(
|
|||||||
});
|
});
|
||||||
|
|
||||||
// wait for emulator to complete booting
|
// wait for emulator to complete booting
|
||||||
await waitForDevice(emulatorBootTimeout);
|
await waitForDevice(port, emulatorBootTimeout);
|
||||||
await exec.exec(`adb shell input keyevent 82`);
|
await adb(port, `shell input keyevent 82`);
|
||||||
|
|
||||||
if (disableAnimations) {
|
if (disableAnimations) {
|
||||||
console.log('Disabling animations.');
|
console.log('Disabling animations.');
|
||||||
await exec.exec(`adb shell settings put global window_animation_scale 0.0`);
|
await adb(port, `shell settings put global window_animation_scale 0.0`);
|
||||||
await exec.exec(`adb shell settings put global transition_animation_scale 0.0`);
|
await adb(port, `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 animator_duration_scale 0.0`);
|
||||||
}
|
}
|
||||||
if (disableSpellChecker) {
|
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) {
|
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 {
|
} finally {
|
||||||
console.log(`::endgroup::`);
|
console.log(`::endgroup::`);
|
||||||
@@ -99,10 +100,10 @@ export async function launchEmulator(
|
|||||||
/**
|
/**
|
||||||
* Kills the running emulator on the default port.
|
* Kills the running emulator on the default port.
|
||||||
*/
|
*/
|
||||||
export async function killEmulator(): Promise<void> {
|
export async function killEmulator(port: number): Promise<void> {
|
||||||
try {
|
try {
|
||||||
console.log(`::group::Terminate Emulator`);
|
console.log(`::group::Terminate Emulator`);
|
||||||
await exec.exec(`adb -s emulator-5554 emu kill`);
|
await 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 {
|
||||||
@@ -110,10 +111,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.
|
* 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 booted = false;
|
||||||
let attempts = 0;
|
let attempts = 0;
|
||||||
const retryInterval = 2; // retry every 2 seconds
|
const retryInterval = 2; // retry every 2 seconds
|
||||||
@@ -121,7 +126,7 @@ async function waitForDevice(emulatorBootTimeout: number): Promise<void> {
|
|||||||
while (!booted) {
|
while (!booted) {
|
||||||
try {
|
try {
|
||||||
let result = '';
|
let result = '';
|
||||||
await exec.exec(`adb shell getprop sys.boot_completed`, [], {
|
await exec.exec(`adb -s emulator-${port} shell getprop sys.boot_completed`, [], {
|
||||||
listeners: {
|
listeners: {
|
||||||
stdout: (data: Buffer) => {
|
stdout: (data: Buffer) => {
|
||||||
result += data.toString();
|
result += data.toString();
|
||||||
|
|||||||
+13
-1
@@ -2,9 +2,12 @@ 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_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 MAX_PORT = 5584;
|
||||||
|
export const PREVIEW_API_LEVELS: Array<string> = ['Tiramisu', 'UpsideDownCake', 'VanillaIceCream'];
|
||||||
|
|
||||||
export function checkApiLevel(apiLevel: string): void {
|
export function checkApiLevel(apiLevel: string): void {
|
||||||
if (apiLevel.startsWith('UpsideDownCake') || apiLevel === 'TiramisuPrivacySandbox') return;
|
if (PREVIEW_API_LEVELS.some((previewLevel) => apiLevel.startsWith(previewLevel))) return;
|
||||||
if (isNaN(Number(apiLevel)) || !Number.isInteger(Number(apiLevel))) {
|
if (isNaN(Number(apiLevel)) || !Number.isInteger(Number(apiLevel))) {
|
||||||
throw new Error(`Unexpected API level: '${apiLevel}'.`);
|
throw new Error(`Unexpected API level: '${apiLevel}'.`);
|
||||||
}
|
}
|
||||||
@@ -37,6 +40,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 {
|
export function checkDisableAnimations(disableAnimations: string): void {
|
||||||
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'.`);
|
||||||
|
|||||||
+14
-3
@@ -12,6 +12,8 @@ import {
|
|||||||
checkChannel,
|
checkChannel,
|
||||||
checkEnableHardwareKeyboard,
|
checkEnableHardwareKeyboard,
|
||||||
checkDiskSize,
|
checkDiskSize,
|
||||||
|
checkPort,
|
||||||
|
MIN_PORT,
|
||||||
} from './input-validator';
|
} from './input-validator';
|
||||||
import { launchEmulator, killEmulator } from './emulator-manager';
|
import { launchEmulator, killEmulator } from './emulator-manager';
|
||||||
import * as exec from '@actions/exec';
|
import * as exec from '@actions/exec';
|
||||||
@@ -20,6 +22,7 @@ import { getChannelId } from './channel-id-mapper';
|
|||||||
import { accessSync, constants } from 'fs';
|
import { accessSync, constants } from 'fs';
|
||||||
|
|
||||||
async function run() {
|
async function run() {
|
||||||
|
let port: number = MIN_PORT;
|
||||||
try {
|
try {
|
||||||
console.log(`::group::Configure emulator`);
|
console.log(`::group::Configure emulator`);
|
||||||
let linuxSupportKVM = false;
|
let linuxSupportKVM = false;
|
||||||
@@ -93,6 +96,11 @@ async function run() {
|
|||||||
const emulatorBootTimeout = parseInt(core.getInput('emulator-boot-timeout'), 10);
|
const emulatorBootTimeout = parseInt(core.getInput('emulator-boot-timeout'), 10);
|
||||||
console.log(`Emulator boot timeout: ${emulatorBootTimeout}`);
|
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
|
// emulator options
|
||||||
const emulatorOptions = core.getInput('emulator-options').trim();
|
const emulatorOptions = core.getInput('emulator-options').trim();
|
||||||
console.log(`emulator options: ${emulatorOptions}`);
|
console.log(`emulator options: ${emulatorOptions}`);
|
||||||
@@ -210,6 +218,7 @@ async function run() {
|
|||||||
avdName,
|
avdName,
|
||||||
forceAvdCreation,
|
forceAvdCreation,
|
||||||
emulatorBootTimeout,
|
emulatorBootTimeout,
|
||||||
|
port,
|
||||||
emulatorOptions,
|
emulatorOptions,
|
||||||
disableAnimations,
|
disableAnimations,
|
||||||
disableSpellchecker,
|
disableSpellchecker,
|
||||||
@@ -226,17 +235,19 @@ async function run() {
|
|||||||
for (const script of scripts) {
|
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 "${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) {
|
} catch (error) {
|
||||||
core.setFailed(error instanceof Error ? error.message : (error as string));
|
core.setFailed(error instanceof Error ? error.message : (error as string));
|
||||||
}
|
}
|
||||||
|
|
||||||
// finally kill the emulator
|
// finally kill the emulator
|
||||||
await killEmulator();
|
await killEmulator(port);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// kill the emulator so the action can exit
|
// kill the emulator so the action can exit
|
||||||
await killEmulator();
|
await killEmulator(port);
|
||||||
core.setFailed(error instanceof Error ? error.message : (error as string));
|
core.setFailed(error instanceof Error ? error.message : (error as string));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ 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 = '34.0.0';
|
const BUILD_TOOLS_VERSION = '35.0.0';
|
||||||
// SDK command-line tools 11.0
|
// SDK command-line tools 16.0
|
||||||
const CMDLINE_TOOLS_URL_MAC = 'https://dl.google.com/android/repository/commandlinetools-mac-10406996_latest.zip';
|
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-10406996_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,
|
* Installs & updates the Android SDK for the macOS platform, including SDK platform for the chosen API level, latest build tools, platform tools, Android Emulator,
|
||||||
@@ -19,10 +19,6 @@ export async function installAndroidSdk(apiLevel: string, target: string, arch:
|
|||||||
const isOnMac = process.platform === 'darwin';
|
const isOnMac = process.platform === 'darwin';
|
||||||
const isArm = process.arch === 'arm64';
|
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`;
|
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.');
|
||||||
@@ -36,6 +32,7 @@ export async function installAndroidSdk(apiLevel: string, target: string, arch:
|
|||||||
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`);
|
||||||
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
|
||||||
@@ -43,7 +40,7 @@ export async function installAndroidSdk(apiLevel: string, target: string, arch:
|
|||||||
|
|
||||||
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 > /dev/null"`);
|
await 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"`);
|
await exec.exec(`sh -c \\"sdkmanager --install emulator --channel=${channelId} > /dev/null"`);
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ apply plugin: 'kotlin-android'
|
|||||||
android {
|
android {
|
||||||
namespace = "com.example.testapp"
|
namespace = "com.example.testapp"
|
||||||
|
|
||||||
compileSdkVersion 34
|
compileSdkVersion 35
|
||||||
buildToolsVersion "34.0.0"
|
buildToolsVersion "35.0.0"
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.example.testapp"
|
applicationId "com.example.testapp"
|
||||||
minSdkVersion 15
|
minSdkVersion 21
|
||||||
targetSdkVersion 34
|
targetSdkVersion 35
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName "1.0"
|
versionName "1.0"
|
||||||
|
|
||||||
@@ -30,8 +30,7 @@ tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
implementation 'androidx.appcompat:appcompat:1.7.0'
|
||||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ buildscript {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:8.2.0'
|
classpath 'com.android.tools.build:gradle:8.7.0'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.21"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.20"
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// in the individual module build.gradle files
|
||||||
|
|||||||
+1
-1
@@ -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.5-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|||||||
Reference in New Issue
Block a user