Skip to content

Commit

Permalink
Merge branch 'main' into release/v2
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
ychescale9 committed May 7, 2021
2 parents d279995 + 751bef6 commit 599839e
Show file tree
Hide file tree
Showing 11 changed files with 94 additions and 19 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# 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).
Expand Down
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 **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.

This action automates the process by doing the following:
Expand Down Expand Up @@ -90,13 +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**. |
| `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". |
| `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`. |
| `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`. |
| `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. |
| `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` |
Expand Down Expand Up @@ -138,5 +139,8 @@ These are some of the open-source projects using (or used) **Android Emulator Ru
- [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.
21 changes: 21 additions & 0 deletions __tests__/input-validator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
it('Throws if emulator-build is not a number', () => {
const func = () => {
Expand Down
7 changes: 5 additions & 2 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: 'Android Emulator Runner'
description: 'Installs, configures and starts an Android Emulator directly on macOS virtual machines.'
author: 'Reactive Circus'
branding:
icon: 'smartphone'
icon: 'smartphone'
color: 'green'
inputs:
api-level:
Expand All @@ -12,7 +12,7 @@ inputs:
description: 'target of the system image - default, google_apis or playstore'
default: 'default'
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'
profile:
description: 'hardware profile used for creating the AVD - e.g. `Nexus 6`'
Expand All @@ -30,6 +30,9 @@ inputs:
disable-animations:
description: 'whether to disable animations - true or false'
default: 'true'
disable-spellchecker:
description: Whether to disable spellchecker - `true` or `false`.
default: 'false'
emulator-build:
description: 'build number of a specific version of the emulator binary to use - e.g. `6061023` for emulator v29.3.0.0'
working-directory:
Expand Down
5 changes: 4 additions & 1 deletion lib/emulator-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const EMULATOR_BOOT_TIMEOUT_SECONDS = 600;
/**
* Creates and launches a new AVD instance with the specified configurations.
*/
function launchEmulator(apiLevel, target, arch, profile, cores, sdcardPathOrSize, avdName, emulatorOptions, disableAnimations) {
function launchEmulator(apiLevel, target, arch, profile, cores, sdcardPathOrSize, avdName, emulatorOptions, disableAnimations, disableSpellChecker) {
return __awaiter(this, void 0, void 0, function* () {
// create a new AVD
const profileOption = profile.trim() !== '' ? `--device '${profile}'` : '';
Expand Down Expand Up @@ -69,6 +69,9 @@ function launchEmulator(apiLevel, target, arch, profile, cores, sdcardPathOrSize
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`);
}
if (disableSpellChecker) {
yield exec.exec(`adb shell settings put secure spell_checker_enabled 0`);
}
});
}
exports.launchEmulator = launchEmulator;
Expand Down
15 changes: 12 additions & 3 deletions lib/input-validator.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"use strict";
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.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) {
if (isNaN(Number(apiLevel)) || !Number.isInteger(Number(apiLevel))) {
throw new Error(`Unexpected API level: '${apiLevel}'.`);
Expand All @@ -26,14 +26,23 @@ function checkArch(arch) {
}
exports.checkArch = checkArch;
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'.`);
}
}
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) {
if (isNaN(Number(emulatorBuild)) || !Number.isInteger(Number(emulatorBuild))) {
throw new Error(`Unexpected emulator build: '${emulatorBuild}'.`);
}
}
exports.checkEmulatorBuild = checkEmulatorBuild;
function isValidBoolean(value) {
return value === 'true' || value === 'false';
}
11 changes: 9 additions & 2 deletions lib/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ function run() {
input_validator_1.checkDisableAnimations(disableAnimationsInput);
const disableAnimations = disableAnimationsInput === 'true';
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
const emulatorBuildInput = core.getInput('emulator-build');
if (emulatorBuildInput) {
Expand Down Expand Up @@ -115,15 +120,17 @@ function run() {
// install SDK
yield sdk_installer_1.installAndroidSdk(apiLevel, target, arch, emulatorBuild, ndkVersion, cmakeVersion);
// launch an emulator
yield emulator_manager_1.launchEmulator(apiLevel, target, arch, profile, cores, sdcardPathOrSize, avdName, emulatorOptions, disableAnimations);
yield emulator_manager_1.launchEmulator(apiLevel, target, arch, profile, cores, sdcardPathOrSize, avdName, emulatorOptions, disableAnimations, disableSpellchecker);
// execute the custom script
try {
// move to custom working directory if set
if (workingDirectory) {
process.chdir(workingDirectory);
}
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) {
Expand Down
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion src/emulator-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ export async function launchEmulator(
sdcardPathOrSize: string,
avdName: string,
emulatorOptions: string,
disableAnimations: boolean
disableAnimations: boolean,
disableSpellChecker: boolean
): Promise<void> {
// create a new AVD
const profileOption = profile.trim() !== '' ? `--device '${profile}'` : '';
Expand Down Expand Up @@ -57,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 animator_duration_scale 0.0`);
}
if (disableSpellChecker) {
await exec.exec(`adb shell settings put secure spell_checker_enabled 0`);
}
}

/**
Expand Down
14 changes: 12 additions & 2 deletions src/input-validator.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export const MIN_API_LEVEL = 15;
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 {
if (isNaN(Number(apiLevel)) || !Number.isInteger(Number(apiLevel))) {
Expand All @@ -24,13 +24,23 @@ export function checkArch(arch: 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'.`);
}
}

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 {
if (isNaN(Number(emulatorBuild)) || !Number.isInteger(Number(emulatorBuild))) {
throw new Error(`Unexpected emulator build: '${emulatorBuild}'.`);
}
}

function isValidBoolean(value: string): boolean {
return value === 'true' || value === 'false';
}
14 changes: 11 additions & 3 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as core from '@actions/core';
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 * as exec from '@actions/exec';
import { parseScript } from './script-parser';
Expand Down Expand Up @@ -61,6 +61,12 @@ async function run() {
const disableAnimations = disableAnimationsInput === 'true';
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
const emulatorBuildInput = core.getInput('emulator-build');
if (emulatorBuildInput) {
Expand Down Expand Up @@ -102,7 +108,7 @@ async function run() {
await installAndroidSdk(apiLevel, target, arch, emulatorBuild, ndkVersion, cmakeVersion);

// launch an emulator
await launchEmulator(apiLevel, target, arch, profile, cores, sdcardPathOrSize, avdName, emulatorOptions, disableAnimations);
await launchEmulator(apiLevel, target, arch, profile, cores, sdcardPathOrSize, avdName, emulatorOptions, disableAnimations, disableSpellchecker);

// execute the custom script
try {
Expand All @@ -111,7 +117,9 @@ async function run() {
process.chdir(workingDirectory);
}
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) {
core.setFailed(error.message);
Expand Down

0 comments on commit 599839e

Please sign in to comment.