Compare commits

..

20 Commits

Author SHA1 Message Date
Yang 599839e428 Merge branch 'main' into release/v2
* main:
  Prepare for release 2.16.0.
  Add support for arm64-v8a for Apple Silicon Macs
  Add option to disable spellcheck
  Bump y18n from 4.0.0 to 4.0.1
  Update README.md
  Add google/android-fhir to the list of projects that use android-emulator-runner (#132)
  Add Wikipedia app to list of projects.
  Add kiwix-android to the list of projects
  Avoid Wrapping Script Code In Quotes
2021-05-07 18:32:02 +10:00
Yang 751bef68f6 Prepare for release 2.16.0. 2021-05-07 18:31:11 +10:00
Cihat Gündüz bbf0887eb9 Add support for arm64-v8a for Apple Silicon Macs
Fixes https://github.com/ReactiveCircus/android-emulator-runner/issues/145
2021-04-17 13:54:28 +10:00
Afzal Najam 84b9418977 Add option to disable spellcheck
Add input-validator and tests
2021-04-08 00:44:27 +10:00
dependabot[bot] a732f125bb Bump y18n from 4.0.0 to 4.0.1
Bumps [y18n](https://github.com/yargs/y18n) from 4.0.0 to 4.0.1.
- [Release notes](https://github.com/yargs/y18n/releases)
- [Changelog](https://github.com/yargs/y18n/blob/master/CHANGELOG.md)
- [Commits](https://github.com/yargs/y18n/commits)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-31 16:32:08 +11:00
Yang 5201a33a90 Update README.md 2021-03-24 09:29:59 +11:00
Jing Tang cf46dc4c18 Add google/android-fhir to the list of projects that use android-emulator-runner (#132) 2021-03-24 07:51:43 +11:00
Dmitry Brant f790770d0f Add Wikipedia app to list of projects. 2021-03-18 12:14:00 +11:00
Ayush Shrivastava 505c899f12 Add kiwix-android to the list of projects
Add kiwix-android to the list of projects that use ReactiveCircus-android-emulator-runner
2021-03-10 12:22:34 +11:00
Brian Dickens 0d47f0813d Avoid Wrapping Script Code In Quotes
The exec() command only runs a program with literal argument strings.
It does not know how to do things like expanding environment variables,
or piping/redirection.  Hence the way to get "shell intelligence"
is to use exec() to run the `sh` process with the command line string
as a parameter.

But the method being used to do this was:

    exec.exec(`sh -c \\"${script}"`)

This has problems, because wrapping the script in quotes creates
issues when the script itself contains quotes.  Escaping the string
correctly is a non-trivial problem, which also would create "noise"
in the debug output which would add confusion.

http://mywiki.wooledge.org/BashFAQ/050

The easiest way to work around this here is to use the array form of
exec() to pass exactly two parameters to `sh`.  No manipulation of the
script string is needed with this approach:

    exec.exec("sh", ["-c", script])

There are more cases in the project of `sh -c` usage which should also
be changed, and likely abstracted (shellExec()?)  But this small patch
just fixes the most important case for the user-provided script.

---

Additionally, this removes the escaped backslash (`\\`) from the
start of the executed command.  That was presumably to suppress the
use of aliases:

https://unix.stackexchange.com/questions/524254/why-are-backslashes-included-in-this-shell-script

But because this is invoking a non-interactive shell session, aliases
would not apply.  Hence the extra backslash shouldn't be needed.
2021-03-09 18:49:23 +11:00
Yang d2799957d6 Merge branch 'main' into release/v2
* main:
  Prepare for release 2.15.0.
  Change default cores to 2, add cores in workflow
  Add option to modify the number of cores
2021-03-05 10:41:47 +11:00
Yang 838986e303 Prepare for release 2.15.0. 2021-03-05 10:40:10 +11:00
Afzal Najam 27a0759d85 Change default cores to 2, add cores in workflow 2021-03-03 08:41:55 +11:00
Afzal Najam d85239a9fb Add option to modify the number of cores 2021-03-03 08:41:55 +11:00
Yang 08b092e904 Merge branch 'main' into release/v2
* main:
  Prepare for release 2.14.3.
  Always add path for cmdline-tools to support macos-11.0.
  Gradle 6.8.
  Add Hash Checker app projects to consumers list
  Add LeakCanary to adopter list
2021-01-13 19:29:01 +11:00
Yang 91b2b2f06f Prepare for release 2.14.3. 2021-01-13 19:28:11 +11:00
Yang 205fbe1b31 Always add path for cmdline-tools to support macos-11.0. 2021-01-13 19:24:10 +11:00
Yang efc0dae5fa Gradle 6.8. 2021-01-09 12:41:16 +11:00
fartem 4281471e5b Add Hash Checker app projects to consumers list 2021-01-09 10:03:23 +11:00
Yang 7f22785663 Add LeakCanary to adopter list 2021-01-06 23:10:03 +11:00
15 changed files with 139 additions and 32 deletions
+2 -1
View File
@@ -30,7 +30,7 @@ jobs:
- target: google_apis - target: google_apis
api-level: 29 api-level: 29
include: include:
- os: macos-latest - os: macos-11.0
api-level: 30 api-level: 30
target: google_apis target: google_apis
- os: macos-latest - os: macos-latest
@@ -71,6 +71,7 @@ jobs:
target: ${{ matrix.target }} target: ${{ matrix.target }}
arch: x86 arch: x86
profile: Nexus 6 profile: Nexus 6
cores: 2
sdcard-path-or-size: 100M sdcard-path-or-size: 100M
avd-name: test avd-name: test
emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim -camera-back none emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim -camera-back none
+14
View File
@@ -1,5 +1,19 @@
# Change Log # Change Log
## v2.16.0
* Avoid wrapping script code in quotes - [#134](https://github.com/ReactiveCircus/android-emulator-runner/pull/134) @hostilefork
* Add option to disable spellcheck - [#143](https://github.com/ReactiveCircus/android-emulator-runner/pull/143) @AfzalivE
* Add support for arm64-v8a for Apple Silicon Macs - [#146](https://github.com/ReactiveCircus/android-emulator-runner/pull/146) @Jeehut
## v2.15.0
* Added support for specifying the number of cores to use for the emulator - [#130](https://github.com/ReactiveCircus/android-emulator-runner/pull/130).
## v2.14.3
* Support `macos-11.0` (Big Sur) runner - [#124](https://github.com/ReactiveCircus/android-emulator-runner/pull/124).
## v2.14.2 ## v2.14.2
* Support API 28 system images with `google_apis` or `google_apis_playstore` target - [#117](https://github.com/ReactiveCircus/android-emulator-runner/pull/117). * Support API 28 system images with `google_apis` or `google_apis_playstore` target - [#117](https://github.com/ReactiveCircus/android-emulator-runner/pull/117).
+17 -9
View File
@@ -8,7 +8,7 @@ A GitHub Action for installing, configuring and running hardware-accelerated And
The old ARM-based emulators were slow and are no longer supported by Google. The modern Intel Atom (x86 and x86_64) emulators require hardware acceleration (HAXM on Mac & Windows, QEMU on Linux) from the host to run fast. This presents a challenge on CI as to be able to run hardware accelerated emulators within a docker container, **KVM** must be supported by the host VM which isn't the case for cloud-based CI providers due to infrastructural limits. If you want to learn more about this, here's an article I wrote: [Running Android Instrumented Tests on CI](https://dev.to/ychescale9/running-android-emulators-on-ci-from-bitrise-io-to-github-actions-3j76). The old ARM-based emulators were slow and are no longer supported by Google. The modern Intel Atom (x86 and x86_64) emulators require hardware acceleration (HAXM on Mac & Windows, QEMU on Linux) from the host to run fast. This presents a challenge on CI as to be able to run hardware accelerated emulators within a docker container, **KVM** must be supported by the host VM which isn't the case for cloud-based CI providers due to infrastructural limits. If you want to learn more about this, here's an article I wrote: [Running Android Instrumented Tests on CI](https://dev.to/ychescale9/running-android-emulators-on-ci-from-bitrise-io-to-github-actions-3j76).
The **macOS** VM provided by **GitHub Actions** has **HAXM** installed so we are able to create a new AVD instance, launch an emulator with hardware acceleration, and run our Android The **macOS** VM provided by **GitHub Actions** has **HAXM** installed so we are able to create a new AVD instance, launch an emulator with hardware acceleration, and run our Android
tests directly on the VM. tests directly on the VM.
This action automates the process by doing the following: This action automates the process by doing the following:
@@ -90,12 +90,14 @@ jobs:
|-|-|-|-| |-|-|-|-|
| `api-level` | Required | N/A | API level of the platform system image - e.g. 23 for Android Marshmallow, 29 for Android 10. **Minimum API level supported is 15**. | | `api-level` | Required | N/A | API level of the platform 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` or `playstore`. | | `target` | Optional | `default` | Target of the system image - `default`, `google_apis` or `playstore`. |
| `arch` | Optional | `x86` | CPU architecture of the system image - `x86` or `x86_64`. Note that `x86_64` image is only available for API 21+. | | `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` and refer to the results under "Available Android Virtual Devices". | | `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` and refer to the results under "Available Android Virtual Devices". |
| `cores` | Optional | 2 | Number of cores to use for the emulator (`hw.cpu.ncore` in config.ini). |
| `sdcard-path-or-size` | Optional | N/A | Path to the SD card image for this AVD or the size of a new SD card image to create for this AVD, in KB or MB, denoted with K or M. - e.g. `path/to/sdcard`, or `1000M`. | | `sdcard-path-or-size` | Optional | N/A | Path to the SD card image for this AVD or the size of a new SD card image to create for this AVD, in KB or MB, denoted with K or M. - e.g. `path/to/sdcard`, or `1000M`. |
| `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. |
| `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`. |
| `emulator-build` | Optional | N/A | Build number of a specific version of the emulator binary to use e.g. `6061023` for emulator v29.3.0.0. | | `emulator-build` | Optional | N/A | Build number of a specific version of the emulator binary to use e.g. `6061023` for emulator v29.3.0.0. |
| `working-directory` | Optional | `./` | A custom working directory - e.g. `./android` if your root Gradle project is under the `./android` sub-directory within your repository. | | `working-directory` | Optional | `./` | A custom working directory - e.g. `./android` if your root Gradle project is under the `./android` sub-directory within your repository. |
| `ndk` | Optional | N/A | Version of NDK to install - e.g. `21.0.6113669` | | `ndk` | Optional | N/A | Version of NDK to install - e.g. `21.0.6113669` |
@@ -115,24 +117,30 @@ For a longer answer please refer to [this issue](https://github.com/ReactiveCirc
These are some of the open-source projects using (or used) **Android Emulator Runner**: These are some of the open-source projects using (or used) **Android Emulator Runner**:
- [coil-kt/coil](https://github.com/coil-kt/coil/blob/master/.github/workflows/) - [coil-kt/coil](https://github.com/coil-kt/coil/blob/master/.github/workflows)
- [cashapp/sqldelight](https://github.com/cashapp/sqldelight/blob/master/.github/workflows/) - [cashapp/sqldelight](https://github.com/cashapp/sqldelight/blob/master/.github/workflows)
- [square/workflow](https://github.com/square/workflow/blob/main/.github/workflows/) - [square/workflow-kotlin](https://github.com/square/workflow-kotlin/tree/main/.github/workflows)
- [square/retrofit](https://github.com/square/retrofit/blob/master/.github/workflows/) - [square/retrofit](https://github.com/square/retrofit/blob/master/.github/workflows)
- [natario1/CameraView](https://github.com/natario1/CameraView/tree/master/.github/workflows) - [natario1/CameraView](https://github.com/natario1/CameraView/tree/master/.github/workflows)
- [natario1/Transcoder](https://github.com/natario1/Transcoder/tree/master/.github/workflows) - [natario1/Transcoder](https://github.com/natario1/Transcoder/tree/master/.github/workflows)
- [chrisbanes/insetter](https://github.com/chrisbanes/insetter/tree/main/.github/workflows) - [chrisbanes/insetter](https://github.com/chrisbanes/insetter/tree/main/.github/workflows)
- [slackhq/keeper](https://github.com/slackhq/keeper/tree/main/.github/workflows) - [slackhq/keeper](https://github.com/slackhq/keeper/tree/main/.github/workflows)
- [android/compose-samples](https://github.com/android/compose-samples/tree/main/.github/workflows) - [android/compose-samples](https://github.com/android/compose-samples/tree/main/.github/workflows)
- [ReactiveCircus/streamlined](https://github.com/ReactiveCircus/streamlined/tree/main/.github/workflows) - [ReactiveCircus/streamlined](https://github.com/ReactiveCircus/streamlined/tree/main/.github/workflows)
- [ReactiveCircus/FlowBinding](https://github.com/ReactiveCircus/FlowBinding) - [ReactiveCircus/FlowBinding](https://github.com/ReactiveCircus/FlowBinding/tree/main/.github/workflows)
- [JakeWharton/RxBinding](https://github.com/JakeWharton/RxBinding/tree/master/.github/workflows) - [JakeWharton/RxBinding](https://github.com/JakeWharton/RxBinding/tree/master/.github/workflows)
- [vinaygaba/Learn-Jetpack-Compose-By-Example](https://github.com/vinaygaba/Learn-Jetpack-Compose-By-Example/) - [vinaygaba/Learn-Jetpack-Compose-By-Example](https://github.com/vinaygaba/Learn-Jetpack-Compose-By-Example/tree/master/.github/workflows)
- [ashishb/adb-enhanced](https://github.com/ashishb/adb-enhanced/tree/master/.github/workflows) - [ashishb/adb-enhanced](https://github.com/ashishb/adb-enhanced/tree/master/.github/workflows)
- [vgaidarji/ci-matters](https://github.com/vgaidarji/ci-matters/blob/master/.github/workflows/main.yaml) - [vgaidarji/ci-matters](https://github.com/vgaidarji/ci-matters/blob/master/.github/workflows/main.yaml)
- [simpledotorg/simple-android](https://github.com/simpledotorg/simple-android) - [simpledotorg/simple-android](https://github.com/simpledotorg/simple-android/tree/master/.github/workflows)
- [cashapp/copper](https://github.com/cashapp/copper/blob/trunk/.github/workflows/build.yaml) - [cashapp/copper](https://github.com/cashapp/copper/blob/trunk/.github/workflows/build.yaml)
- [square/radiography](https://github.com/square/radiography/blob/main/.github/workflows/android.yml) - [square/radiography](https://github.com/square/radiography/blob/main/.github/workflows/android.yml)
- [Shopify/android-testify](https://github.com/Shopify/android-testify/blob/master/.github/workflows/sample_build.yml) - [Shopify/android-testify](https://github.com/Shopify/android-testify/blob/master/.github/workflows/sample_build.yml)
- [square/leakcanary](https://github.com/square/leakcanary/tree/main/.github/workflows)
- [hash-checker/hash-checker](https://github.com/hash-checker/hash-checker/tree/master/.github/workflows)
- [hash-checker/hash-checker-lite](https://github.com/hash-checker/hash-checker-lite/tree/master/.github/workflows)
- [Kiwix/kiwix-android](https://github.com/kiwix/kiwix-android/blob/develop/.github/workflows)
- [wikimedia/apps-android-wikipedia](https://github.com/wikimedia/apps-android-wikipedia/blob/master/.github/workflows)
- [google/android-fhir](https://github.com/google/android-fhir/tree/master/.github/workflows)
If you are using **Android Emulator Runner** and want your project included in the list, please feel free to create an issue or open a pull request. If you are using **Android Emulator Runner** and want your project included in the list, please feel free to create an issue or open a pull request.
+21
View File
@@ -103,6 +103,27 @@ describe('disable-animations validator tests', () => {
}); });
}); });
describe('disable-spellchecker validator tests', () => {
it('Throws if disable-spellchecker is not a boolean', () => {
const func = () => {
validator.checkDisableSpellchecker('yes');
};
expect(func).toThrowError(`Input for input.disable-spellchecker should be either 'true' or 'false'.`);
});
it('Validates successfully if disable-spellchecker is either true or false', () => {
const func1 = () => {
validator.checkDisableSpellchecker('true');
};
expect(func1).not.toThrow();
const func2 = () => {
validator.checkDisableSpellchecker('false');
};
expect(func2).not.toThrow();
});
});
describe('emulator-build validator tests', () => { describe('emulator-build validator tests', () => {
it('Throws if emulator-build is not a number', () => { it('Throws if emulator-build is not a number', () => {
const func = () => { const func = () => {
+8 -2
View File
@@ -2,7 +2,7 @@ name: 'Android Emulator Runner'
description: 'Installs, configures and starts an Android Emulator directly on macOS virtual machines.' description: 'Installs, configures and starts an Android Emulator directly on macOS virtual machines.'
author: 'Reactive Circus' author: 'Reactive Circus'
branding: branding:
icon: 'smartphone' icon: 'smartphone'
color: 'green' color: 'green'
inputs: inputs:
api-level: api-level:
@@ -12,10 +12,13 @@ inputs:
description: 'target of the system image - default, google_apis or playstore' description: 'target of the system image - default, google_apis or playstore'
default: 'default' default: 'default'
arch: arch:
description: 'CPU architecture of the system image - x86 or x86_64' description: 'CPU architecture of the system image - x86, x86_64 or arm64-v8a'
default: 'x86' default: 'x86'
profile: profile:
description: 'hardware profile used for creating the AVD - e.g. `Nexus 6`' description: 'hardware profile used for creating the AVD - e.g. `Nexus 6`'
cores:
description: 'the number of cores to use for the emulator'
default: 2
sdcard-path-or-size: sdcard-path-or-size:
description: 'path to the SD card image for this AVD or the size of a new SD card image to create for this AVD, in KB or MB, denoted with K or M. - e.g. `path/to/sdcard`, or `1000M`' description: 'path to the SD card image for this AVD or the size of a new SD card image to create for this AVD, in KB or MB, denoted with K or M. - e.g. `path/to/sdcard`, or `1000M`'
avd-name: avd-name:
@@ -27,6 +30,9 @@ inputs:
disable-animations: disable-animations:
description: 'whether to disable animations - true or false' description: 'whether to disable animations - true or false'
default: 'true' default: 'true'
disable-spellchecker:
description: Whether to disable spellchecker - `true` or `false`.
default: 'false'
emulator-build: emulator-build:
description: 'build number of a specific version of the emulator binary to use - e.g. `6061023` for emulator v29.3.0.0' description: 'build number of a specific version of the emulator binary to use - e.g. `6061023` for emulator v29.3.0.0'
working-directory: working-directory:
+7 -1
View File
@@ -34,13 +34,16 @@ const EMULATOR_BOOT_TIMEOUT_SECONDS = 600;
/** /**
* 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, sdcardPathOrSize, avdName, emulatorOptions, disableAnimations) { function launchEmulator(apiLevel, target, arch, profile, cores, sdcardPathOrSize, avdName, emulatorOptions, disableAnimations, disableSpellChecker) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
// create a new AVD // create a new AVD
const profileOption = profile.trim() !== '' ? `--device '${profile}'` : ''; const profileOption = profile.trim() !== '' ? `--device '${profile}'` : '';
const sdcardPathOrSizeOption = sdcardPathOrSize.trim() !== '' ? `--sdcard '${sdcardPathOrSize}'` : ''; const sdcardPathOrSizeOption = sdcardPathOrSize.trim() !== '' ? `--sdcard '${sdcardPathOrSize}'` : '';
console.log(`Creating AVD.`); console.log(`Creating AVD.`);
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}" --abi '${target}/${arch}' --package 'system-images;android-${apiLevel};${target};${arch}' ${profileOption} ${sdcardPathOrSizeOption}"`);
if (cores) {
yield exec.exec(`sh -c \\"printf 'hw.cpu.ncore=${cores}\n' >> ~/.android/avd/"${avdName}".avd"/config.ini`);
}
// start emulator // start emulator
console.log('Starting emulator.'); console.log('Starting emulator.');
// turn off hardware acceleration on Linux // turn off hardware acceleration on Linux
@@ -66,6 +69,9 @@ function launchEmulator(apiLevel, target, arch, profile, sdcardPathOrSize, avdNa
yield exec.exec(`adb shell settings put global transition_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 exec.exec(`adb shell settings put global animator_duration_scale 0.0`);
} }
if (disableSpellChecker) {
yield exec.exec(`adb shell settings put secure spell_checker_enabled 0`);
}
}); });
} }
exports.launchEmulator = launchEmulator; exports.launchEmulator = launchEmulator;
+12 -3
View File
@@ -1,9 +1,9 @@
"use strict"; "use strict";
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.checkEmulatorBuild = exports.checkDisableAnimations = exports.checkArch = exports.checkTarget = exports.checkApiLevel = exports.VALID_ARCHS = exports.VALID_TARGETS = exports.MIN_API_LEVEL = void 0; exports.checkEmulatorBuild = exports.checkDisableSpellchecker = exports.checkDisableAnimations = exports.checkArch = exports.checkTarget = exports.checkApiLevel = 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', 'google_apis_playstore']; exports.VALID_TARGETS = ['default', 'google_apis', 'google_apis_playstore'];
exports.VALID_ARCHS = ['x86', 'x86_64']; exports.VALID_ARCHS = ['x86', 'x86_64', 'arm64-v8a'];
function checkApiLevel(apiLevel) { function checkApiLevel(apiLevel) {
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}'.`);
@@ -26,14 +26,23 @@ function checkArch(arch) {
} }
exports.checkArch = checkArch; exports.checkArch = checkArch;
function checkDisableAnimations(disableAnimations) { function checkDisableAnimations(disableAnimations) {
if (disableAnimations !== 'true' && disableAnimations !== 'false') { if (!isValidBoolean(disableAnimations)) {
throw new Error(`Input for input.disable-animations should be either 'true' or 'false'.`); throw new Error(`Input for input.disable-animations should be either 'true' or 'false'.`);
} }
} }
exports.checkDisableAnimations = checkDisableAnimations; exports.checkDisableAnimations = checkDisableAnimations;
function checkDisableSpellchecker(disableSpellchecker) {
if (!isValidBoolean(disableSpellchecker)) {
throw new Error(`Input for input.disable-spellchecker should be either 'true' or 'false'.`);
}
}
exports.checkDisableSpellchecker = checkDisableSpellchecker;
function checkEmulatorBuild(emulatorBuild) { function checkEmulatorBuild(emulatorBuild) {
if (isNaN(Number(emulatorBuild)) || !Number.isInteger(Number(emulatorBuild))) { if (isNaN(Number(emulatorBuild)) || !Number.isInteger(Number(emulatorBuild))) {
throw new Error(`Unexpected emulator build: '${emulatorBuild}'.`); throw new Error(`Unexpected emulator build: '${emulatorBuild}'.`);
} }
} }
exports.checkEmulatorBuild = checkEmulatorBuild; exports.checkEmulatorBuild = checkEmulatorBuild;
function isValidBoolean(value) {
return value === 'true' || value === 'false';
}
+12 -2
View File
@@ -63,6 +63,9 @@ function run() {
// Hardware profile used for creating the AVD // Hardware profile used for creating the AVD
const profile = core.getInput('profile'); const profile = core.getInput('profile');
console.log(`Hardware profile: ${profile}`); console.log(`Hardware profile: ${profile}`);
// Number of cores to use for emulator
const cores = core.getInput('cores');
console.log(`Cores: ${cores}`);
// SD card path or size used for creating the AVD // SD card path or size used for creating the AVD
const sdcardPathOrSize = core.getInput('sdcard-path-or-size'); const sdcardPathOrSize = core.getInput('sdcard-path-or-size');
console.log(`SD card path or size: ${sdcardPathOrSize}`); console.log(`SD card path or size: ${sdcardPathOrSize}`);
@@ -77,6 +80,11 @@ function run() {
input_validator_1.checkDisableAnimations(disableAnimationsInput); input_validator_1.checkDisableAnimations(disableAnimationsInput);
const disableAnimations = disableAnimationsInput === 'true'; const disableAnimations = disableAnimationsInput === 'true';
console.log(`disable animations: ${disableAnimations}`); console.log(`disable animations: ${disableAnimations}`);
// disable spellchecker
const disableSpellcheckerInput = core.getInput('disable-spellchecker');
input_validator_1.checkDisableSpellchecker(disableSpellcheckerInput);
const disableSpellchecker = disableSpellcheckerInput === 'true';
console.log(`disable spellchecker: ${disableSpellchecker}`);
// emulator build // emulator build
const emulatorBuildInput = core.getInput('emulator-build'); const emulatorBuildInput = core.getInput('emulator-build');
if (emulatorBuildInput) { if (emulatorBuildInput) {
@@ -112,7 +120,7 @@ function run() {
// install SDK // install SDK
yield sdk_installer_1.installAndroidSdk(apiLevel, target, arch, emulatorBuild, ndkVersion, cmakeVersion); yield sdk_installer_1.installAndroidSdk(apiLevel, target, arch, emulatorBuild, ndkVersion, cmakeVersion);
// launch an emulator // launch an emulator
yield emulator_manager_1.launchEmulator(apiLevel, target, arch, profile, sdcardPathOrSize, avdName, emulatorOptions, disableAnimations); yield emulator_manager_1.launchEmulator(apiLevel, target, arch, profile, cores, sdcardPathOrSize, avdName, emulatorOptions, disableAnimations, disableSpellchecker);
// execute the custom script // execute the custom script
try { try {
// move to custom working directory if set // move to custom working directory if set
@@ -120,7 +128,9 @@ function run() {
process.chdir(workingDirectory); process.chdir(workingDirectory);
} }
for (const script of scripts) { for (const script of scripts) {
yield exec.exec(`sh -c \\"${script}"`); // use array form to avoid various quote escaping problems
// caused by exec(`sh -c "${script}"`)
yield exec.exec('sh', ['-c', script]);
} }
} }
catch (error) { catch (error) {
+2 -2
View File
@@ -54,9 +54,9 @@ function installAndroidSdk(apiLevel, target, arch, emulatorBuild, ndkVersion, cm
const downloadPath = yield tc.downloadTool(sdkUrl); const downloadPath = yield tc.downloadTool(sdkUrl);
yield tc.extractZip(downloadPath, cmdlineToolsPath); yield tc.extractZip(downloadPath, cmdlineToolsPath);
yield io.mv(`${cmdlineToolsPath}/cmdline-tools`, `${cmdlineToolsPath}/latest`); yield io.mv(`${cmdlineToolsPath}/cmdline-tools`, `${cmdlineToolsPath}/latest`);
// add paths for commandline-tools and platform-tools
core.addPath(`${cmdlineToolsPath}/latest:${cmdlineToolsPath}/latest/bin:${process.env.ANDROID_SDK_ROOT}/platform-tools`);
} }
// add paths for commandline-tools and platform-tools
core.addPath(`${cmdlineToolsPath}/latest:${cmdlineToolsPath}/latest/bin:${process.env.ANDROID_SDK_ROOT}/platform-tools`);
// additional permission and license requirements for Linux // additional permission and license requirements for Linux
const sdkPreviewLicensePath = `${process.env.ANDROID_SDK_ROOT}/licenses/android-sdk-preview-license`; const sdkPreviewLicensePath = `${process.env.ANDROID_SDK_ROOT}/licenses/android-sdk-preview-license`;
if (!isOnMac && !fs.existsSync(sdkPreviewLicensePath)) { if (!isOnMac && !fs.existsSync(sdkPreviewLicensePath)) {
+3 -3
View File
@@ -11309,9 +11309,9 @@
"dev": true "dev": true
}, },
"y18n": { "y18n": {
"version": "4.0.0", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz",
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==",
"dev": true "dev": true
}, },
"yallist": { "yallist": {
+10 -1
View File
@@ -10,10 +10,12 @@ export async function launchEmulator(
target: string, target: string,
arch: string, arch: string,
profile: string, profile: string,
cores: string,
sdcardPathOrSize: string, sdcardPathOrSize: string,
avdName: string, avdName: string,
emulatorOptions: string, emulatorOptions: string,
disableAnimations: boolean disableAnimations: boolean,
disableSpellChecker: boolean
): Promise<void> { ): Promise<void> {
// create a new AVD // create a new AVD
const profileOption = profile.trim() !== '' ? `--device '${profile}'` : ''; const profileOption = profile.trim() !== '' ? `--device '${profile}'` : '';
@@ -23,6 +25,10 @@ export async function launchEmulator(
`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}" --abi '${target}/${arch}' --package 'system-images;android-${apiLevel};${target};${arch}' ${profileOption} ${sdcardPathOrSizeOption}"`
); );
if (cores) {
await exec.exec(`sh -c \\"printf 'hw.cpu.ncore=${cores}\n' >> ~/.android/avd/"${avdName}".avd"/config.ini`);
}
// start emulator // start emulator
console.log('Starting emulator.'); console.log('Starting emulator.');
@@ -52,6 +58,9 @@ export async function launchEmulator(
await exec.exec(`adb shell settings put global transition_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 exec.exec(`adb shell settings put global animator_duration_scale 0.0`);
} }
if (disableSpellChecker) {
await exec.exec(`adb shell settings put secure spell_checker_enabled 0`);
}
} }
/** /**
+12 -2
View File
@@ -1,6 +1,6 @@
export const MIN_API_LEVEL = 15; export const MIN_API_LEVEL = 15;
export const VALID_TARGETS: Array<string> = ['default', 'google_apis', 'google_apis_playstore']; export const VALID_TARGETS: Array<string> = ['default', 'google_apis', 'google_apis_playstore'];
export const VALID_ARCHS: Array<string> = ['x86', 'x86_64']; export const VALID_ARCHS: Array<string> = ['x86', 'x86_64', 'arm64-v8a'];
export function checkApiLevel(apiLevel: string): void { export function checkApiLevel(apiLevel: string): void {
if (isNaN(Number(apiLevel)) || !Number.isInteger(Number(apiLevel))) { if (isNaN(Number(apiLevel)) || !Number.isInteger(Number(apiLevel))) {
@@ -24,13 +24,23 @@ export function checkArch(arch: string): void {
} }
export function checkDisableAnimations(disableAnimations: string): void { export function checkDisableAnimations(disableAnimations: string): void {
if (disableAnimations !== 'true' && disableAnimations !== 'false') { 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'.`);
} }
} }
export function checkDisableSpellchecker(disableSpellchecker: string): void {
if (!isValidBoolean(disableSpellchecker)) {
throw new Error(`Input for input.disable-spellchecker should be either 'true' or 'false'.`);
}
}
export function checkEmulatorBuild(emulatorBuild: string): void { export function checkEmulatorBuild(emulatorBuild: string): void {
if (isNaN(Number(emulatorBuild)) || !Number.isInteger(Number(emulatorBuild))) { if (isNaN(Number(emulatorBuild)) || !Number.isInteger(Number(emulatorBuild))) {
throw new Error(`Unexpected emulator build: '${emulatorBuild}'.`); throw new Error(`Unexpected emulator build: '${emulatorBuild}'.`);
} }
} }
function isValidBoolean(value: string): boolean {
return value === 'true' || value === 'false';
}
+15 -3
View File
@@ -1,6 +1,6 @@
import * as core from '@actions/core'; import * as core from '@actions/core';
import { installAndroidSdk } from './sdk-installer'; import { installAndroidSdk } from './sdk-installer';
import { checkApiLevel, checkTarget, checkArch, checkDisableAnimations, checkEmulatorBuild } from './input-validator'; import { checkApiLevel, checkTarget, checkArch, checkDisableAnimations, checkEmulatorBuild, checkDisableSpellchecker } 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';
import { parseScript } from './script-parser'; import { parseScript } from './script-parser';
@@ -39,6 +39,10 @@ async function run() {
const profile = core.getInput('profile'); const profile = core.getInput('profile');
console.log(`Hardware profile: ${profile}`); console.log(`Hardware profile: ${profile}`);
// Number of cores to use for emulator
const cores = core.getInput('cores');
console.log(`Cores: ${cores}`);
// SD card path or size used for creating the AVD // SD card path or size used for creating the AVD
const sdcardPathOrSize = core.getInput('sdcard-path-or-size'); const sdcardPathOrSize = core.getInput('sdcard-path-or-size');
console.log(`SD card path or size: ${sdcardPathOrSize}`); console.log(`SD card path or size: ${sdcardPathOrSize}`);
@@ -57,6 +61,12 @@ async function run() {
const disableAnimations = disableAnimationsInput === 'true'; const disableAnimations = disableAnimationsInput === 'true';
console.log(`disable animations: ${disableAnimations}`); console.log(`disable animations: ${disableAnimations}`);
// disable spellchecker
const disableSpellcheckerInput = core.getInput('disable-spellchecker');
checkDisableSpellchecker(disableSpellcheckerInput);
const disableSpellchecker = disableSpellcheckerInput === 'true';
console.log(`disable spellchecker: ${disableSpellchecker}`);
// emulator build // emulator build
const emulatorBuildInput = core.getInput('emulator-build'); const emulatorBuildInput = core.getInput('emulator-build');
if (emulatorBuildInput) { if (emulatorBuildInput) {
@@ -98,7 +108,7 @@ async function run() {
await installAndroidSdk(apiLevel, target, arch, emulatorBuild, ndkVersion, cmakeVersion); await installAndroidSdk(apiLevel, target, arch, emulatorBuild, ndkVersion, cmakeVersion);
// launch an emulator // launch an emulator
await launchEmulator(apiLevel, target, arch, profile, sdcardPathOrSize, avdName, emulatorOptions, disableAnimations); await launchEmulator(apiLevel, target, arch, profile, cores, sdcardPathOrSize, avdName, emulatorOptions, disableAnimations, disableSpellchecker);
// execute the custom script // execute the custom script
try { try {
@@ -107,7 +117,9 @@ async function run() {
process.chdir(workingDirectory); process.chdir(workingDirectory);
} }
for (const script of scripts) { for (const script of scripts) {
await exec.exec(`sh -c \\"${script}"`); // use array form to avoid various quote escaping problems
// caused by exec(`sh -c "${script}"`)
await exec.exec('sh', ['-c', script]);
} }
} catch (error) { } catch (error) {
core.setFailed(error.message); core.setFailed(error.message);
+3 -2
View File
@@ -26,10 +26,11 @@ export async function installAndroidSdk(apiLevel: number, target: string, arch:
const downloadPath = await tc.downloadTool(sdkUrl); const downloadPath = await tc.downloadTool(sdkUrl);
await tc.extractZip(downloadPath, cmdlineToolsPath); await tc.extractZip(downloadPath, cmdlineToolsPath);
await io.mv(`${cmdlineToolsPath}/cmdline-tools`, `${cmdlineToolsPath}/latest`); await io.mv(`${cmdlineToolsPath}/cmdline-tools`, `${cmdlineToolsPath}/latest`);
// add paths for commandline-tools and platform-tools
core.addPath(`${cmdlineToolsPath}/latest:${cmdlineToolsPath}/latest/bin:${process.env.ANDROID_SDK_ROOT}/platform-tools`);
} }
// add paths for commandline-tools and platform-tools
core.addPath(`${cmdlineToolsPath}/latest:${cmdlineToolsPath}/latest/bin:${process.env.ANDROID_SDK_ROOT}/platform-tools`);
// additional permission and license requirements for Linux // additional permission and license requirements for Linux
const sdkPreviewLicensePath = `${process.env.ANDROID_SDK_ROOT}/licenses/android-sdk-preview-license`; const sdkPreviewLicensePath = `${process.env.ANDROID_SDK_ROOT}/licenses/android-sdk-preview-license`;
if (!isOnMac && !fs.existsSync(sdkPreviewLicensePath)) { if (!isOnMac && !fs.existsSync(sdkPreviewLicensePath)) {
+1 -1
View File
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-bin.zip