Compare commits

..

17 Commits

Author SHA1 Message Date
Yang e790971012 Merge branch 'main' into release/v2
* main:
  Prepare for release 2.24.0.
  Add option to specify AVD heap size (#245)
  Gradle 7.4.2.
  Bump GH actions (#240)
  Bump minimist from 1.2.5 to 1.2.6 (#237)
  Update dependencies in test-fixture.
  Add more .gitignore rules (#231)
  Update README for using with gradle/gradle-build-action (#230)
2022-04-30 23:35:35 +10:00
Yang f30ff064b1 Prepare for release 2.24.0. 2022-04-30 23:20:42 +10:00
Tim Malseed cc3dd8cbef Add option to specify AVD heap size (#245) 2022-04-26 23:36:59 +10:00
Yang 689ee971a8 Gradle 7.4.2. 2022-04-16 01:29:20 +10:00
Goooler 4ea6abdd76 Bump GH actions (#240) 2022-04-07 00:43:35 +10:00
dependabot[bot] 280a99ecb8 Bump minimist from 1.2.5 to 1.2.6 (#237)
Bumps [minimist](https://github.com/substack/minimist) from 1.2.5 to 1.2.6.
- [Release notes](https://github.com/substack/minimist/releases)
- [Commits](https://github.com/substack/minimist/compare/1.2.5...1.2.6)

---
updated-dependencies:
- dependency-name: minimist
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-07 00:26:40 +10:00
Yang aacab61b72 Update dependencies in test-fixture. 2022-03-04 16:36:46 +11:00
Goooler a24195edb9 Add more .gitignore rules (#231)
* Add more .gitignore rules

* Update .gitignore
2022-03-04 16:31:18 +11:00
Goooler 20da7eafac Update README for using with gradle/gradle-build-action (#230)
* Update README for using with gradle/gradle-build-action

* Code highlighting in README

* Add a step in the snippet
2022-03-04 16:31:00 +11:00
Yang 76c2bf6f95 Merge branch 'main' into release/v2
* main:
  Prepare for release 2.23.0.
  Add missing generated .js files.
  Add GHA commands to group logs (#224)
  wikimedia have likely renamed master -> main (#221)
  Allow increasing main storage size (sdcard is separate storage) (#219)
  Update to SDK command-line tools 6.0. (#213)
  Update build tools to 32.0.0. Update text-fixture compileSdkVersion and targetSdkVersion to 32. (#212)
2022-02-18 01:49:20 +11:00
Yang 08a23a6229 Prepare for release 2.23.0. 2022-02-18 01:48:36 +11:00
Yang 5bd7b4aaec Add missing generated .js files. 2022-02-18 01:35:48 +11:00
Michael Kaye 1e2ef7249a Add GHA commands to group logs (#224) 2022-02-17 20:03:29 +11:00
Michael Kaye 3045dbfaba wikimedia have likely renamed master -> main (#221) 2022-02-11 08:41:38 +11:00
Vilius Sutkus '89 4e0dd2c343 Allow increasing main storage size (sdcard is separate storage) (#219)
* Allow increasing disk size - Issue #184

* Fix checkDiskSize validator

* Fix lint errors

* Size modifier without a number is unacceptable

"G" is not the same as "1G"

* Implement checkDiskSize validator tests
2022-02-05 16:07:40 +11:00
Yang 62d97062b0 Update to SDK command-line tools 6.0. (#213) 2022-01-27 16:22:43 +11:00
Yang e35fd713f4 Update build tools to 32.0.0. Update text-fixture compileSdkVersion and targetSdkVersion to 32. (#212) 2022-01-26 11:26:11 +11:00
25 changed files with 445 additions and 281 deletions
+4 -3
View File
@@ -48,7 +48,7 @@ jobs:
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v2 uses: actions/checkout@v3
- name: validate gradle wrapper - name: validate gradle wrapper
uses: gradle/wrapper-validation-action@v1 uses: gradle/wrapper-validation-action@v1
@@ -61,10 +61,11 @@ jobs:
npm test npm test
- name: Java 15 - name: Java 15
uses: actions/setup-java@v1 uses: actions/setup-java@v3
with: with:
distribution: 'zulu'
java-version: 15 java-version: 15
- uses: actions/cache@v2 - uses: actions/cache@v3
id: avd-cache id: avd-cache
with: with:
path: | path: |
+4 -3
View File
@@ -39,7 +39,7 @@ jobs:
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v2 uses: actions/checkout@v3
- name: validate gradle wrapper - name: validate gradle wrapper
uses: gradle/wrapper-validation-action@v1 uses: gradle/wrapper-validation-action@v1
@@ -52,10 +52,11 @@ jobs:
npm test npm test
- name: Java 15 - name: Java 15
uses: actions/setup-java@v1 uses: actions/setup-java@v3
with: with:
distribution: 'zulu'
java-version: 15 java-version: 15
- uses: actions/cache@v2 - uses: actions/cache@v3
with: with:
path: | path: |
~/.gradle/caches ~/.gradle/caches
+10
View File
@@ -91,3 +91,13 @@ typings/
# DynamoDB Local files # DynamoDB Local files
.dynamodb/ .dynamodb/
# Mac
.DS_Store
# Vscode
.vscode
# IntelliJ IDEA
.idea
*.iml
+11
View File
@@ -1,5 +1,16 @@
# Change Log # Change Log
## v2.24.0
* Add option to specify `heap-size` for the AVD. - [#245](https://github.com/ReactiveCircus/android-emulator-runner/pull/245) @timusus.
## v2.23.0
* Update build tools to `32.0.0`. - [#212](https://github.com/ReactiveCircus/android-emulator-runner/pull/212)
* Update SDK command-line tools to `6.0`. - [#213](https://github.com/ReactiveCircus/android-emulator-runner/pull/213)
* Add option to specify `disk-size` for the AVD. - [#219](https://github.com/ReactiveCircus/android-emulator-runner/pull/219) @ViliusSutkus89.
* Improve logging by grouping log lines. - [#224](https://github.com/ReactiveCircus/android-emulator-runner/pull/224) @michaelkaye.
## v2.22.0 ## v2.22.0
* Add option to enable hardware keyboard. - [#209](https://github.com/ReactiveCircus/android-emulator-runner/pull/209) (upstreamed from the [Doist fork](https://github.com/Doist/android-emulator-runner/commit/4b6ca99f0d657662beca3eb0c22d8e254fbd5b31)). * Add option to enable hardware keyboard. - [#209](https://github.com/ReactiveCircus/android-emulator-runner/pull/209) (upstreamed from the [Doist fork](https://github.com/Doist/android-emulator-runner/commit/4b6ca99f0d657662beca3eb0c22d8e254fbd5b31)).
+18 -20
View File
@@ -26,13 +26,13 @@ It is recommended to run this action on a **macOS** VM, e.g. `macos-latest`, `ma
A workflow that uses **android-emulator-runner** to run your instrumented tests on **API 29**: A workflow that uses **android-emulator-runner** to run your instrumented tests on **API 29**:
``` ```yml
jobs: jobs:
test: test:
runs-on: macos-latest runs-on: macos-latest
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v2 uses: actions/checkout@v3
- name: run tests - name: run tests
uses: reactivecircus/android-emulator-runner@v2 uses: reactivecircus/android-emulator-runner@v2
@@ -43,7 +43,7 @@ jobs:
We can also leverage GitHub Actions's build matrix to test across multiple configurations: We can also leverage GitHub Actions's build matrix to test across multiple configurations:
``` ```yml
jobs: jobs:
test: test:
runs-on: macos-latest runs-on: macos-latest
@@ -53,7 +53,7 @@ jobs:
target: [default, google_apis] target: [default, google_apis]
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v2 uses: actions/checkout@v3
- name: run tests - name: run tests
uses: reactivecircus/android-emulator-runner@v2 uses: reactivecircus/android-emulator-runner@v2
@@ -67,13 +67,13 @@ jobs:
If you need specific versions of **NDK** and **CMake** installed: If you need specific versions of **NDK** and **CMake** installed:
``` ```yml
jobs: jobs:
test: test:
runs-on: macos-latest runs-on: macos-latest
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v2 uses: actions/checkout@v3
- name: run tests - name: run tests
uses: reactivecircus/android-emulator-runner@v2 uses: reactivecircus/android-emulator-runner@v2
@@ -86,11 +86,12 @@ 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 an `actions/cache@v2` step for caching the `avd` 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)
2. add a `reactivecircus/android-emulator-runner@v2` step to generate a clean snapshot - specify `emulator-options` without `no-snapshot` 2. add an `actions/cache@v3` step for caching the `avd`
3. add another `reactivecircus/android-emulator-runner@v2` step to run your tests using existing AVD / snapshot - specify `emulator-options` with `no-snapshot-save` 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`
``` ```yml
jobs: jobs:
test: test:
runs-on: macos-latest runs-on: macos-latest
@@ -99,18 +100,13 @@ jobs:
api-level: [21, 23, 29] api-level: [21, 23, 29]
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v2 uses: actions/checkout@v3
- name: Gradle cache - name: Gradle cache
uses: actions/cache@v2 uses: gradle/gradle-build-action@v2
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*') }}-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }}-${{ hashFiles('**/buildSrc/**/*.kt') }}
- name: AVD cache - name: AVD cache
uses: actions/cache@v2 uses: actions/cache@v3
id: avd-cache id: avd-cache
with: with:
path: | path: |
@@ -148,7 +144,9 @@ jobs:
| `profile` | Optional | N/A | Hardware profile used for creating the AVD - e.g. `Nexus 6`. For a list of all profiles available, run `avdmanager list device`. | | `profile` | Optional | N/A | Hardware profile used for creating the AVD - e.g. `Nexus 6`. For a list of all profiles available, run `avdmanager list device`. |
| `cores` | Optional | 2 | Number of cores to use for the emulator (`hw.cpu.ncore` in config.ini). | | `cores` | Optional | 2 | Number of cores to use for the emulator (`hw.cpu.ncore` in config.ini). |
| `ram-size` | Optional | N/A | Size of RAM to use for this AVD, in KB or MB, denoted with K or M. - e.g. `2048M` | | `ram-size` | Optional | N/A | Size of RAM to use for this AVD, in KB or MB, denoted with K or M. - e.g. `2048M` |
| `heap-size` | Optional | N/A | Heap size to use for this AVD, in KB or MB, denoted with K or M. - e.g. `512M` |
| `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`. |
| `disk-size` | Optional | N/A | Disk size to use for this AVD. Either in bytes or KB, MB or GB, when denoted with K, M or G. - e.g. `2048M` |
| `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-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`. |
@@ -199,7 +197,7 @@ These are some of the open-source projects using (or used) **Android Emulator Ru
- [hash-checker/hash-checker](https://github.com/hash-checker/hash-checker/tree/master/.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) - [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) - [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) - [wikimedia/apps-android-wikipedia](https://github.com/wikimedia/apps-android-wikipedia/blob/main/.github/workflows)
- [google/android-fhir](https://github.com/google/android-fhir/tree/master/.github/workflows) - [google/android-fhir](https://github.com/google/android-fhir/tree/master/.github/workflows)
- [google/accompanist](https://github.com/google/accompanist/blob/main/.github/workflows) - [google/accompanist](https://github.com/google/accompanist/blob/main/.github/workflows)
- [dotanuki-labs/norris](https://github.com/dotanuki-labs/norris/blob/master/.github/workflows/main.yml) - [dotanuki-labs/norris](https://github.com/dotanuki-labs/norris/blob/master/.github/workflows/main.yml)
+51
View File
@@ -270,3 +270,54 @@ describe('emulator-build validator tests', () => {
expect(func).not.toThrow(); expect(func).not.toThrow();
}); });
}); });
describe('checkDiskSize validator tests', () => {
it('Empty size is acceptable, means default', () => {
const func = () => {
validator.checkDiskSize('');
};
expect(func).not.toThrow();
});
it('Numbers means bytes', () => {
expect(() => {
validator.checkDiskSize('8000000000');
}).not.toThrow();
});
it('Uppercase size modifier', () => {
expect(() => {
validator.checkDiskSize('8000000K');
}).not.toThrow();
expect(() => {
validator.checkDiskSize('8000M');
}).not.toThrow();
expect(() => {
validator.checkDiskSize('8G');
}).not.toThrow();
});
it('Lowercase size modifier', () => {
expect(() => {
validator.checkDiskSize('8000000k');
}).not.toThrow();
expect(() => {
validator.checkDiskSize('8000m');
}).not.toThrow();
expect(() => {
validator.checkDiskSize('8g');
}).not.toThrow();
});
it('Modifier without a number is unacceptable', () => {
expect(() => {
validator.checkDiskSize('G');
}).toThrowError(`Unexpected disk size: 'G'.`);
});
it('Double modifier is unacceptable', () => {
expect(() => {
validator.checkDiskSize('14gg');
}).toThrowError(`Unexpected disk size: '14gg'.`);
});
});
+4
View File
@@ -21,8 +21,12 @@ inputs:
default: 2 default: 2
ram-size: ram-size:
description: 'size of RAM to use for this AVD, in KB or MB, denoted with K or M. - e.g. `2048M`' description: 'size of RAM to use for this AVD, in KB or MB, denoted with K or M. - e.g. `2048M`'
heap-size:
description: 'size of heap to use for this AVD in MB. - e.g. `512M`'
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`'
disk-size:
description: 'disk size to use for this AVD. Either in bytes or KB, MB or GB, when denoted with K, M or G'
avd-name: avd-name:
description: 'custom AVD name used for creating the Android Virtual Device' description: 'custom AVD name used for creating the Android Virtual Device'
default: 'test' default: 'test'
+60 -44
View File
@@ -35,55 +35,67 @@ 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, cores, ramSize, sdcardPathOrSize, avdName, forceAvdCreation, emulatorOptions, disableAnimations, disableSpellChecker, disableLinuxHardwareAcceleration, enableHardwareKeyboard) { function launchEmulator(apiLevel, target, arch, profile, cores, ramSize, heapSize, sdcardPathOrSize, diskSize, avdName, forceAvdCreation, emulatorOptions, disableAnimations, disableSpellChecker, disableLinuxHardwareAcceleration, enableHardwareKeyboard) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
// create a new AVD if AVD directory does not already exist or forceAvdCreation is true try {
const avdPath = `${process.env.ANDROID_AVD_HOME}/${avdName}.avd`; console.log(`::group::Launch Emulator`);
if (!fs.existsSync(avdPath) || forceAvdCreation) { // create a new AVD if AVD directory does not already exist or forceAvdCreation is true
const profileOption = profile.trim() !== '' ? `--device '${profile}'` : ''; const avdPath = `${process.env.ANDROID_AVD_HOME}/${avdName}.avd`;
const sdcardPathOrSizeOption = sdcardPathOrSize.trim() !== '' ? `--sdcard '${sdcardPathOrSize}'` : ''; if (!fs.existsSync(avdPath) || forceAvdCreation) {
console.log(`Creating AVD.`); const profileOption = profile.trim() !== '' ? `--device '${profile}'` : '';
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}"`); const sdcardPathOrSizeOption = sdcardPathOrSize.trim() !== '' ? `--sdcard '${sdcardPathOrSize}'` : '';
} console.log(`Creating AVD.`);
if (cores) { 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 \\"printf 'hw.cpu.ncore=${cores}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`); }
} if (cores) {
if (ramSize) { yield exec.exec(`sh -c \\"printf 'hw.cpu.ncore=${cores}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
yield exec.exec(`sh -c \\"printf 'hw.ramSize=${ramSize}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`); }
} if (ramSize) {
if (enableHardwareKeyboard) { yield exec.exec(`sh -c \\"printf 'hw.ramSize=${ramSize}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
yield exec.exec(`sh -c \\"printf 'hw.keyboard=yes\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`); }
} if (heapSize) {
//turn off hardware acceleration on Linux yield exec.exec(`sh -c \\"printf 'hw.heapSize=${heapSize}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
if (process.platform === 'linux' && disableLinuxHardwareAcceleration) { }
console.log('Disabling Linux hardware acceleration.'); if (enableHardwareKeyboard) {
emulatorOptions += ' -accel off'; yield exec.exec(`sh -c \\"printf 'hw.keyboard=yes\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
} }
// start emulator if (diskSize) {
console.log('Starting emulator.'); yield exec.exec(`sh -c \\"printf 'disk.dataPartition.size=${diskSize}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
yield exec.exec(`sh -c \\"${process.env.ANDROID_SDK_ROOT}/emulator/emulator -avd "${avdName}" ${emulatorOptions} &"`, [], { }
listeners: { //turn off hardware acceleration on Linux
stderr: (data) => { if (process.platform === 'linux' && disableLinuxHardwareAcceleration) {
if (data.toString().includes('invalid command-line parameter')) { console.log('Disabling Linux hardware acceleration.');
throw new Error(data.toString()); emulatorOptions += ' -accel off';
}
// start emulator
console.log('Starting emulator.');
yield exec.exec(`sh -c \\"${process.env.ANDROID_SDK_ROOT}/emulator/emulator -avd "${avdName}" ${emulatorOptions} &"`, [], {
listeners: {
stderr: (data) => {
if (data.toString().includes('invalid command-line parameter')) {
throw new Error(data.toString());
}
} }
} }
});
// wait for emulator to complete booting
yield waitForDevice();
yield exec.exec(`adb shell input keyevent 82`);
if (disableAnimations) {
console.log('Disabling animations.');
yield exec.exec(`adb shell settings put global window_animation_scale 0.0`);
yield exec.exec(`adb shell settings put global transition_animation_scale 0.0`);
yield exec.exec(`adb shell settings put global animator_duration_scale 0.0`);
}
if (disableSpellChecker) {
yield exec.exec(`adb shell settings put secure spell_checker_enabled 0`);
}
if (enableHardwareKeyboard) {
yield exec.exec(`adb shell settings put secure show_ime_with_hard_keyboard 0`);
} }
});
// wait for emulator to complete booting
yield waitForDevice();
yield exec.exec(`adb shell input keyevent 82`);
if (disableAnimations) {
console.log('Disabling animations.');
yield exec.exec(`adb shell settings put global window_animation_scale 0.0`);
yield exec.exec(`adb shell settings put global transition_animation_scale 0.0`);
yield exec.exec(`adb shell settings put global animator_duration_scale 0.0`);
} }
if (disableSpellChecker) { finally {
yield exec.exec(`adb shell settings put secure spell_checker_enabled 0`); console.log(`::endgroup::`);
}
if (enableHardwareKeyboard) {
yield exec.exec(`adb shell settings put secure show_ime_with_hard_keyboard 0`);
} }
}); });
} }
@@ -94,11 +106,15 @@ exports.launchEmulator = launchEmulator;
function killEmulator() { function killEmulator() {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
try { try {
console.log(`::group::Terminate Emulator`);
yield exec.exec(`adb -s emulator-5554 emu kill`); yield exec.exec(`adb -s emulator-5554 emu kill`);
} }
catch (error) { catch (error) {
console.log(error.message); console.log(error.message);
} }
finally {
console.log(`::endgroup::`);
}
}); });
} }
exports.killEmulator = killEmulator; exports.killEmulator = killEmulator;
+18 -1
View File
@@ -1,6 +1,6 @@
"use strict"; "use strict";
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
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.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.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'];
@@ -71,3 +71,20 @@ exports.checkEmulatorBuild = checkEmulatorBuild;
function isValidBoolean(value) { function isValidBoolean(value) {
return value === 'true' || value === 'false'; return value === 'true' || value === 'false';
} }
function checkDiskSize(diskSize) {
// Disk size can be empty - the default value
if (diskSize) {
// Can also be number of bytes
if (isNaN(Number(diskSize)) || !Number.isInteger(Number(diskSize))) {
// Disk size can have a size multiplier at the end K, M or G
const diskSizeUpperCase = diskSize.toUpperCase();
if (diskSizeUpperCase.endsWith('K') || diskSizeUpperCase.endsWith('M') || diskSizeUpperCase.endsWith('G')) {
const diskSizeNoModifier = diskSize.slice(0, -1);
if (0 == diskSizeNoModifier.length || isNaN(Number(diskSizeNoModifier)) || !Number.isInteger(Number(diskSizeNoModifier))) {
throw new Error(`Unexpected disk size: '${diskSize}'.`);
}
}
}
}
}
exports.checkDiskSize = checkDiskSize;
+9 -1
View File
@@ -38,6 +38,7 @@ const channel_id_mapper_1 = require("./channel-id-mapper");
function run() { function run() {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
try { try {
console.log(`::group::Configure emulator`);
// only support running on macOS or Linux // only support running on macOS or Linux
if (process.platform !== 'darwin') { if (process.platform !== 'darwin') {
if (process.platform === 'linux') { if (process.platform === 'linux') {
@@ -70,9 +71,15 @@ function run() {
// RAM to use for AVD // RAM to use for AVD
const ramSize = core.getInput('ram-size'); const ramSize = core.getInput('ram-size');
console.log(`RAM size: ${ramSize}`); console.log(`RAM size: ${ramSize}`);
// Heap size to use for AVD
const heapSize = core.getInput('heap-size');
console.log(`Heap size: ${heapSize}`);
// SD card path or size used for creating the AVD // 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}`);
const diskSize = core.getInput('disk-size');
input_validator_1.checkDiskSize(diskSize);
console.log(`Disk size: ${diskSize}`);
// custom name used for creating the AVD // custom name used for creating the AVD
const avdName = core.getInput('avd-name'); const avdName = core.getInput('avd-name');
console.log(`AVD name: ${avdName}`); console.log(`AVD name: ${avdName}`);
@@ -141,10 +148,11 @@ function run() {
scripts.forEach((script) => __awaiter(this, void 0, void 0, function* () { scripts.forEach((script) => __awaiter(this, void 0, void 0, function* () {
console.log(`${script}`); console.log(`${script}`);
})); }));
console.log(`::endgroup::`);
// install SDK // install SDK
yield sdk_installer_1.installAndroidSdk(apiLevel, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion); yield sdk_installer_1.installAndroidSdk(apiLevel, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion);
// launch an emulator // launch an emulator
yield emulator_manager_1.launchEmulator(apiLevel, target, arch, profile, cores, ramSize, sdcardPathOrSize, avdName, forceAvdCreation, emulatorOptions, disableAnimations, disableSpellchecker, disableLinuxHardwareAcceleration, enableHardwareKeyboard); yield emulator_manager_1.launchEmulator(apiLevel, target, arch, profile, cores, ramSize, heapSize, sdcardPathOrSize, diskSize, avdName, forceAvdCreation, 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
+47 -41
View File
@@ -34,54 +34,60 @@ 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 = '31.0.0'; const BUILD_TOOLS_VERSION = '32.0.0';
const CMDLINE_TOOLS_URL_MAC = 'https://dl.google.com/android/repository/commandlinetools-mac-7583922_latest.zip'; const CMDLINE_TOOLS_URL_MAC = 'https://dl.google.com/android/repository/commandlinetools-mac-8092744_latest.zip';
const CMDLINE_TOOLS_URL_LINUX = 'https://dl.google.com/android/repository/commandlinetools-linux-7583922_latest.zip'; const CMDLINE_TOOLS_URL_LINUX = 'https://dl.google.com/android/repository/commandlinetools-linux-8092744_latest.zip';
/** /**
* Installs & updates the Android SDK for the macOS platform, including SDK platform for the chosen API level, latest build tools, platform tools, Android Emulator, * Installs & updates the Android SDK for the macOS platform, including SDK platform for the chosen API level, latest build tools, platform tools, Android Emulator,
* and the system image for the chosen API level, CPU arch, and target. * and the system image for the chosen API level, CPU arch, and target.
*/ */
function installAndroidSdk(apiLevel, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion) { function installAndroidSdk(apiLevel, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const isOnMac = process.platform === 'darwin'; try {
if (!isOnMac) { console.log(`::group::Install Android SDK`);
yield exec.exec(`sh -c \\"sudo chown $USER:$USER ${process.env.ANDROID_SDK_ROOT} -R`); const isOnMac = process.platform === 'darwin';
if (!isOnMac) {
yield exec.exec(`sh -c \\"sudo chown $USER:$USER ${process.env.ANDROID_SDK_ROOT} -R`);
}
const cmdlineToolsPath = `${process.env.ANDROID_SDK_ROOT}/cmdline-tools`;
if (!fs.existsSync(cmdlineToolsPath)) {
console.log('Installing new cmdline-tools.');
const sdkUrl = isOnMac ? CMDLINE_TOOLS_URL_MAC : CMDLINE_TOOLS_URL_LINUX;
const downloadPath = yield tc.downloadTool(sdkUrl);
yield tc.extractZip(downloadPath, cmdlineToolsPath);
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`);
// set standard AVD path
core.exportVariable('ANDROID_AVD_HOME', `${process.env.HOME}/.android/avd`);
// accept all Android SDK licenses
yield exec.exec(`sh -c \\"yes | sdkmanager --licenses > /dev/null"`);
console.log('Installing latest build tools, platform tools, and platform.');
yield exec.exec(`sh -c \\"sdkmanager --install 'build-tools;${BUILD_TOOLS_VERSION}' platform-tools 'platforms;android-${apiLevel}' > /dev/null"`);
console.log('Installing latest emulator.');
yield exec.exec(`sh -c \\"sdkmanager --install emulator --channel=${channelId} > /dev/null"`);
if (emulatorBuild) {
console.log(`Installing emulator build ${emulatorBuild}.`);
// TODO find out the correct download URLs for all build ids
const downloadUrlSuffix = Number(emulatorBuild.charAt(0)) > 6 ? `_x64-${emulatorBuild}` : `-${emulatorBuild}`;
yield exec.exec(`curl -fo emulator.zip https://dl.google.com/android/repository/emulator-${isOnMac ? 'darwin' : 'linux'}${downloadUrlSuffix}.zip`);
yield exec.exec(`unzip -o -q emulator.zip -d ${process.env.ANDROID_SDK_ROOT}`);
yield io.rmRF('emulator.zip');
}
console.log('Installing system images.');
yield exec.exec(`sh -c \\"sdkmanager --install 'system-images;android-${apiLevel};${target};${arch}' --channel=${channelId} > /dev/null"`);
if (ndkVersion) {
console.log(`Installing NDK ${ndkVersion}.`);
yield exec.exec(`sh -c \\"sdkmanager --install 'ndk;${ndkVersion}' --channel=${channelId} > /dev/null"`);
}
if (cmakeVersion) {
console.log(`Installing CMake ${cmakeVersion}.`);
yield exec.exec(`sh -c \\"sdkmanager --install 'cmake;${cmakeVersion}' --channel=${channelId} > /dev/null"`);
}
} }
const cmdlineToolsPath = `${process.env.ANDROID_SDK_ROOT}/cmdline-tools`; finally {
if (!fs.existsSync(cmdlineToolsPath)) { console.log(`::endgroup::`);
console.log('Installing new cmdline-tools.');
const sdkUrl = isOnMac ? CMDLINE_TOOLS_URL_MAC : CMDLINE_TOOLS_URL_LINUX;
const downloadPath = yield tc.downloadTool(sdkUrl);
yield tc.extractZip(downloadPath, cmdlineToolsPath);
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`);
// set standard AVD path
core.exportVariable('ANDROID_AVD_HOME', `${process.env.HOME}/.android/avd`);
// accept all Android SDK licenses
yield exec.exec(`sh -c \\"yes | sdkmanager --licenses > /dev/null"`);
console.log('Installing latest build tools, platform tools, and platform.');
yield exec.exec(`sh -c \\"sdkmanager --install 'build-tools;${BUILD_TOOLS_VERSION}' platform-tools 'platforms;android-${apiLevel}' > /dev/null"`);
console.log('Installing latest emulator.');
yield exec.exec(`sh -c \\"sdkmanager --install emulator --channel=${channelId} > /dev/null"`);
if (emulatorBuild) {
console.log(`Installing emulator build ${emulatorBuild}.`);
// TODO find out the correct download URLs for all build ids
const downloadUrlSuffix = Number(emulatorBuild.charAt(0)) > 6 ? `_x64-${emulatorBuild}` : `-${emulatorBuild}`;
yield exec.exec(`curl -fo emulator.zip https://dl.google.com/android/repository/emulator-${isOnMac ? 'darwin' : 'linux'}${downloadUrlSuffix}.zip`);
yield exec.exec(`unzip -o -q emulator.zip -d ${process.env.ANDROID_SDK_ROOT}`);
yield io.rmRF('emulator.zip');
}
console.log('Installing system images.');
yield exec.exec(`sh -c \\"sdkmanager --install 'system-images;android-${apiLevel};${target};${arch}' --channel=${channelId} > /dev/null"`);
if (ndkVersion) {
console.log(`Installing NDK ${ndkVersion}.`);
yield exec.exec(`sh -c \\"sdkmanager --install 'ndk;${ndkVersion}' --channel=${channelId} > /dev/null"`);
}
if (cmakeVersion) {
console.log(`Installing CMake ${cmakeVersion}.`);
yield exec.exec(`sh -c \\"sdkmanager --install 'cmake;${cmakeVersion}' --channel=${channelId} > /dev/null"`);
} }
}); });
} }
+3 -3
View File
@@ -74,9 +74,9 @@
} }
}, },
"node_modules/minimist": { "node_modules/minimist": {
"version": "1.2.5", "version": "1.2.6",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
}, },
"node_modules/semver": { "node_modules/semver": {
"version": "6.3.0", "version": "6.3.0",
+6 -2
View File
@@ -70,7 +70,7 @@ module.exports = function (args, opts) {
var o = obj; var o = obj;
for (var i = 0; i < keys.length-1; i++) { for (var i = 0; i < keys.length-1; i++) {
var key = keys[i]; var key = keys[i];
if (key === '__proto__') return; if (isConstructorOrProto(o, key)) return;
if (o[key] === undefined) o[key] = {}; if (o[key] === undefined) o[key] = {};
if (o[key] === Object.prototype || o[key] === Number.prototype if (o[key] === Object.prototype || o[key] === Number.prototype
|| o[key] === String.prototype) o[key] = {}; || o[key] === String.prototype) o[key] = {};
@@ -79,7 +79,7 @@ module.exports = function (args, opts) {
} }
var key = keys[keys.length - 1]; var key = keys[keys.length - 1];
if (key === '__proto__') return; if (isConstructorOrProto(o, key)) return;
if (o === Object.prototype || o === Number.prototype if (o === Object.prototype || o === Number.prototype
|| o === String.prototype) o = {}; || o === String.prototype) o = {};
if (o === Array.prototype) o = []; if (o === Array.prototype) o = [];
@@ -243,3 +243,7 @@ function isNumber (x) {
return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x); return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x);
} }
function isConstructorOrProto (obj, key) {
return key === 'constructor' && typeof obj[key] === 'function' || key === '__proto__';
}
+22 -57
View File
@@ -1,67 +1,16 @@
{ {
"_args": [ "name": "minimist",
[ "version": "1.2.6",
"minimist@1.2.5",
"/Users/Yang/Desktop/Projects/android-emulator-runner"
]
],
"_from": "minimist@1.2.5",
"_id": "minimist@1.2.5",
"_inBundle": false,
"_integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"_location": "/minimist",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "minimist@1.2.5",
"name": "minimist",
"escapedName": "minimist",
"rawSpec": "1.2.5",
"saveSpec": null,
"fetchSpec": "1.2.5"
},
"_requiredBy": [
"/",
"/@babel/core/json5",
"/@cnakazawa/watch",
"/json5",
"/sane"
],
"_resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"_spec": "1.2.5",
"_where": "/Users/Yang/Desktop/Projects/android-emulator-runner",
"author": {
"name": "James Halliday",
"email": "mail@substack.net",
"url": "http://substack.net"
},
"bugs": {
"url": "https://github.com/substack/minimist/issues"
},
"description": "parse argument options", "description": "parse argument options",
"main": "index.js",
"devDependencies": { "devDependencies": {
"covert": "^1.0.0", "covert": "^1.0.0",
"tap": "~0.4.0", "tap": "~0.4.0",
"tape": "^3.5.0" "tape": "^3.5.0"
}, },
"homepage": "https://github.com/substack/minimist",
"keywords": [
"argv",
"getopt",
"parser",
"optimist"
],
"license": "MIT",
"main": "index.js",
"name": "minimist",
"repository": {
"type": "git",
"url": "git://github.com/substack/minimist.git"
},
"scripts": { "scripts": {
"coverage": "covert test/*.js", "test": "tap test/*.js",
"test": "tap test/*.js" "coverage": "covert test/*.js"
}, },
"testling": { "testling": {
"files": "test/*.js", "files": "test/*.js",
@@ -76,5 +25,21 @@
"opera/12" "opera/12"
] ]
}, },
"version": "1.2.5" "repository": {
"type": "git",
"url": "git://github.com/substack/minimist.git"
},
"homepage": "https://github.com/substack/minimist",
"keywords": [
"argv",
"getopt",
"parser",
"optimist"
],
"author": {
"name": "James Halliday",
"email": "mail@substack.net",
"url": "http://substack.net"
},
"license": "MIT"
} }
+4 -1
View File
@@ -34,7 +34,10 @@ $ node example/parse.js -x 3 -y 4 -n5 -abc --beep=boop foo bar baz
Previous versions had a prototype pollution bug that could cause privilege Previous versions had a prototype pollution bug that could cause privilege
escalation in some circumstances when handling untrusted user input. escalation in some circumstances when handling untrusted user input.
Please use version 1.2.3 or later: https://snyk.io/vuln/SNYK-JS-MINIMIST-559764 Please use version 1.2.6 or later:
* https://security.snyk.io/vuln/SNYK-JS-MINIMIST-2429795 (version <=1.2.5)
* https://snyk.io/vuln/SNYK-JS-MINIMIST-559764 (version <=1.2.3)
# methods # methods
+16
View File
@@ -42,3 +42,19 @@ test('proto pollution (constructor)', function (t) {
t.equal(argv.y, undefined); t.equal(argv.y, undefined);
t.end(); t.end();
}); });
test('proto pollution (constructor function)', function (t) {
var argv = parse(['--_.concat.constructor.prototype.y', '123']);
function fnToBeTested() {}
t.equal(fnToBeTested.y, undefined);
t.equal(argv.y, undefined);
t.end();
});
// powered by snyk - https://github.com/backstage/backstage/issues/10343
test('proto pollution (constructor function) snyk', function (t) {
var argv = parse('--_.constructor.constructor.prototype.foo bar'.split(' '));
t.equal((function(){}).foo, undefined);
t.equal(argv.y, undefined);
t.end();
})
+7 -7
View File
@@ -12,7 +12,7 @@
"@actions/core": "^1.6.0", "@actions/core": "^1.6.0",
"@actions/exec": "^1.1.0", "@actions/exec": "^1.1.0",
"@actions/tool-cache": "^1.7.1", "@actions/tool-cache": "^1.7.1",
"minimist": "^1.2.5", "minimist": "^1.2.6",
"set-value": "^4.1.0" "set-value": "^4.1.0"
}, },
"devDependencies": { "devDependencies": {
@@ -8508,9 +8508,9 @@
} }
}, },
"node_modules/minimist": { "node_modules/minimist": {
"version": "1.2.5", "version": "1.2.6",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
}, },
"node_modules/mixin-deep": { "node_modules/mixin-deep": {
"version": "1.3.2", "version": "1.3.2",
@@ -17432,9 +17432,9 @@
} }
}, },
"minimist": { "minimist": {
"version": "1.2.5", "version": "1.2.6",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
}, },
"mixin-deep": { "mixin-deep": {
"version": "1.3.2", "version": "1.3.2",
+1 -1
View File
@@ -24,7 +24,7 @@
"@actions/core": "^1.6.0", "@actions/core": "^1.6.0",
"@actions/exec": "^1.1.0", "@actions/exec": "^1.1.0",
"@actions/tool-cache": "^1.7.1", "@actions/tool-cache": "^1.7.1",
"minimist": "^1.2.5", "minimist": "^1.2.6",
"set-value": "^4.1.0" "set-value": "^4.1.0"
}, },
"devDependencies": { "devDependencies": {
+66 -48
View File
@@ -13,7 +13,9 @@ export async function launchEmulator(
profile: string, profile: string,
cores: string, cores: string,
ramSize: string, ramSize: string,
heapSize: string,
sdcardPathOrSize: string, sdcardPathOrSize: string,
diskSize: string,
avdName: string, avdName: string,
forceAvdCreation: boolean, forceAvdCreation: boolean,
emulatorOptions: string, emulatorOptions: string,
@@ -22,63 +24,76 @@ export async function launchEmulator(
disableLinuxHardwareAcceleration: boolean, disableLinuxHardwareAcceleration: boolean,
enableHardwareKeyboard: boolean enableHardwareKeyboard: boolean
): Promise<void> { ): Promise<void> {
// create a new AVD if AVD directory does not already exist or forceAvdCreation is true try {
const avdPath = `${process.env.ANDROID_AVD_HOME}/${avdName}.avd`; console.log(`::group::Launch Emulator`);
if (!fs.existsSync(avdPath) || forceAvdCreation) { // create a new AVD if AVD directory does not already exist or forceAvdCreation is true
const profileOption = profile.trim() !== '' ? `--device '${profile}'` : ''; const avdPath = `${process.env.ANDROID_AVD_HOME}/${avdName}.avd`;
const sdcardPathOrSizeOption = sdcardPathOrSize.trim() !== '' ? `--sdcard '${sdcardPathOrSize}'` : ''; if (!fs.existsSync(avdPath) || forceAvdCreation) {
console.log(`Creating AVD.`); const profileOption = profile.trim() !== '' ? `--device '${profile}'` : '';
await exec.exec( const sdcardPathOrSizeOption = sdcardPathOrSize.trim() !== '' ? `--sdcard '${sdcardPathOrSize}'` : '';
`sh -c \\"echo no | avdmanager create avd --force -n "${avdName}" --abi '${target}/${arch}' --package 'system-images;android-${apiLevel};${target};${arch}' ${profileOption} ${sdcardPathOrSizeOption}"` console.log(`Creating AVD.`);
); await exec.exec(
} `sh -c \\"echo no | avdmanager create avd --force -n "${avdName}" --abi '${target}/${arch}' --package 'system-images;android-${apiLevel};${target};${arch}' ${profileOption} ${sdcardPathOrSizeOption}"`
);
}
if (cores) { if (cores) {
await exec.exec(`sh -c \\"printf 'hw.cpu.ncore=${cores}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`); await exec.exec(`sh -c \\"printf 'hw.cpu.ncore=${cores}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
} }
if (ramSize) { if (ramSize) {
await exec.exec(`sh -c \\"printf 'hw.ramSize=${ramSize}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`); await exec.exec(`sh -c \\"printf 'hw.ramSize=${ramSize}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
} }
if (enableHardwareKeyboard) { if (heapSize) {
await exec.exec(`sh -c \\"printf 'hw.keyboard=yes\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`); await exec.exec(`sh -c \\"printf 'hw.heapSize=${heapSize}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
} }
//turn off hardware acceleration on Linux if (enableHardwareKeyboard) {
if (process.platform === 'linux' && disableLinuxHardwareAcceleration) { await exec.exec(`sh -c \\"printf 'hw.keyboard=yes\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
console.log('Disabling Linux hardware acceleration.'); }
emulatorOptions += ' -accel off';
}
// start emulator if (diskSize) {
console.log('Starting emulator.'); await exec.exec(`sh -c \\"printf 'disk.dataPartition.size=${diskSize}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
}
await exec.exec(`sh -c \\"${process.env.ANDROID_SDK_ROOT}/emulator/emulator -avd "${avdName}" ${emulatorOptions} &"`, [], { //turn off hardware acceleration on Linux
listeners: { if (process.platform === 'linux' && disableLinuxHardwareAcceleration) {
stderr: (data: Buffer) => { console.log('Disabling Linux hardware acceleration.');
if (data.toString().includes('invalid command-line parameter')) { emulatorOptions += ' -accel off';
throw new Error(data.toString()); }
// start emulator
console.log('Starting emulator.');
await exec.exec(`sh -c \\"${process.env.ANDROID_SDK_ROOT}/emulator/emulator -avd "${avdName}" ${emulatorOptions} &"`, [], {
listeners: {
stderr: (data: Buffer) => {
if (data.toString().includes('invalid command-line parameter')) {
throw new Error(data.toString());
}
} }
} }
});
// wait for emulator to complete booting
await waitForDevice();
await exec.exec(`adb shell input keyevent 82`);
if (disableAnimations) {
console.log('Disabling animations.');
await exec.exec(`adb shell settings put global window_animation_scale 0.0`);
await exec.exec(`adb shell settings put global transition_animation_scale 0.0`);
await exec.exec(`adb shell settings put global animator_duration_scale 0.0`);
} }
}); if (disableSpellChecker) {
await exec.exec(`adb shell settings put secure spell_checker_enabled 0`);
// wait for emulator to complete booting }
await waitForDevice(); if (enableHardwareKeyboard) {
await exec.exec(`adb shell input keyevent 82`); await exec.exec(`adb shell settings put secure show_ime_with_hard_keyboard 0`);
}
if (disableAnimations) { } finally {
console.log('Disabling animations.'); console.log(`::endgroup::`);
await exec.exec(`adb shell settings put global window_animation_scale 0.0`);
await exec.exec(`adb shell settings put global transition_animation_scale 0.0`);
await exec.exec(`adb shell settings put global animator_duration_scale 0.0`);
}
if (disableSpellChecker) {
await exec.exec(`adb shell settings put secure spell_checker_enabled 0`);
}
if (enableHardwareKeyboard) {
await exec.exec(`adb shell settings put secure show_ime_with_hard_keyboard 0`);
} }
} }
@@ -87,9 +102,12 @@ export async function launchEmulator(
*/ */
export async function killEmulator(): Promise<void> { export async function killEmulator(): Promise<void> {
try { try {
console.log(`::group::Terminate Emulator`);
await exec.exec(`adb -s emulator-5554 emu kill`); await exec.exec(`adb -s emulator-5554 emu kill`);
} catch (error) { } catch (error) {
console.log(error.message); console.log(error.message);
} finally {
console.log(`::endgroup::`);
} }
} }
+17
View File
@@ -69,3 +69,20 @@ export function checkEmulatorBuild(emulatorBuild: string): void {
function isValidBoolean(value: string): boolean { function isValidBoolean(value: string): boolean {
return value === 'true' || value === 'false'; return value === 'true' || value === 'false';
} }
export function checkDiskSize(diskSize: string): void {
// Disk size can be empty - the default value
if (diskSize) {
// Can also be number of bytes
if (isNaN(Number(diskSize)) || !Number.isInteger(Number(diskSize))) {
// Disk size can have a size multiplier at the end K, M or G
const diskSizeUpperCase = diskSize.toUpperCase();
if (diskSizeUpperCase.endsWith('K') || diskSizeUpperCase.endsWith('M') || diskSizeUpperCase.endsWith('G')) {
const diskSizeNoModifier: string = diskSize.slice(0, -1);
if (0 == diskSizeNoModifier.length || isNaN(Number(diskSizeNoModifier)) || !Number.isInteger(Number(diskSizeNoModifier))) {
throw new Error(`Unexpected disk size: '${diskSize}'.`);
}
}
}
}
}
+14 -1
View File
@@ -10,7 +10,8 @@ import {
checkDisableLinuxHardwareAcceleration, checkDisableLinuxHardwareAcceleration,
checkForceAvdCreation, checkForceAvdCreation,
checkChannel, checkChannel,
checkEnableHardwareKeyboard checkEnableHardwareKeyboard,
checkDiskSize
} 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';
@@ -19,6 +20,7 @@ import { getChannelId } from './channel-id-mapper';
async function run() { async function run() {
try { try {
console.log(`::group::Configure emulator`);
// only support running on macOS or Linux // only support running on macOS or Linux
if (process.platform !== 'darwin') { if (process.platform !== 'darwin') {
if (process.platform === 'linux') { if (process.platform === 'linux') {
@@ -59,10 +61,18 @@ async function run() {
const ramSize = core.getInput('ram-size'); const ramSize = core.getInput('ram-size');
console.log(`RAM size: ${ramSize}`); console.log(`RAM size: ${ramSize}`);
// Heap size to use for AVD
const heapSize = core.getInput('heap-size');
console.log(`Heap size: ${heapSize}`);
// SD card path or size used for creating the AVD // 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}`);
const diskSize = core.getInput('disk-size');
checkDiskSize(diskSize);
console.log(`Disk size: ${diskSize}`);
// custom name used for creating the AVD // custom name used for creating the AVD
const avdName = core.getInput('avd-name'); const avdName = core.getInput('avd-name');
console.log(`AVD name: ${avdName}`); console.log(`AVD name: ${avdName}`);
@@ -143,6 +153,7 @@ async function run() {
scripts.forEach(async (script: string) => { scripts.forEach(async (script: string) => {
console.log(`${script}`); console.log(`${script}`);
}); });
console.log(`::endgroup::`);
// install SDK // install SDK
await installAndroidSdk(apiLevel, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion); await installAndroidSdk(apiLevel, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion);
@@ -155,7 +166,9 @@ async function run() {
profile, profile,
cores, cores,
ramSize, ramSize,
heapSize,
sdcardPathOrSize, sdcardPathOrSize,
diskSize,
avdName, avdName,
forceAvdCreation, forceAvdCreation,
emulatorOptions, emulatorOptions,
+47 -42
View File
@@ -4,63 +4,68 @@ 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 = '31.0.0'; const BUILD_TOOLS_VERSION = '32.0.0';
const CMDLINE_TOOLS_URL_MAC = 'https://dl.google.com/android/repository/commandlinetools-mac-7583922_latest.zip'; const CMDLINE_TOOLS_URL_MAC = 'https://dl.google.com/android/repository/commandlinetools-mac-8092744_latest.zip';
const CMDLINE_TOOLS_URL_LINUX = 'https://dl.google.com/android/repository/commandlinetools-linux-7583922_latest.zip'; const CMDLINE_TOOLS_URL_LINUX = 'https://dl.google.com/android/repository/commandlinetools-linux-8092744_latest.zip';
/** /**
* Installs & updates the Android SDK for the macOS platform, including SDK platform for the chosen API level, latest build tools, platform tools, Android Emulator, * Installs & updates the Android SDK for the macOS platform, including SDK platform for the chosen API level, latest build tools, platform tools, Android Emulator,
* and the system image for the chosen API level, CPU arch, and target. * and the system image for the chosen API level, CPU arch, and target.
*/ */
export async function installAndroidSdk(apiLevel: number, target: string, arch: string, channelId: number, emulatorBuild?: string, ndkVersion?: string, cmakeVersion?: string): Promise<void> { export async function installAndroidSdk(apiLevel: number, target: string, arch: string, channelId: number, emulatorBuild?: string, ndkVersion?: string, cmakeVersion?: string): Promise<void> {
const isOnMac = process.platform === 'darwin'; try {
console.log(`::group::Install Android SDK`);
const isOnMac = process.platform === 'darwin';
if (!isOnMac) { if (!isOnMac) {
await exec.exec(`sh -c \\"sudo chown $USER:$USER ${process.env.ANDROID_SDK_ROOT} -R`); await exec.exec(`sh -c \\"sudo chown $USER:$USER ${process.env.ANDROID_SDK_ROOT} -R`);
} }
const cmdlineToolsPath = `${process.env.ANDROID_SDK_ROOT}/cmdline-tools`; const cmdlineToolsPath = `${process.env.ANDROID_SDK_ROOT}/cmdline-tools`;
if (!fs.existsSync(cmdlineToolsPath)) { if (!fs.existsSync(cmdlineToolsPath)) {
console.log('Installing new cmdline-tools.'); console.log('Installing new cmdline-tools.');
const sdkUrl = isOnMac ? CMDLINE_TOOLS_URL_MAC : CMDLINE_TOOLS_URL_LINUX; const sdkUrl = isOnMac ? CMDLINE_TOOLS_URL_MAC : CMDLINE_TOOLS_URL_LINUX;
const downloadPath = await tc.downloadTool(sdkUrl); const downloadPath = 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 // add paths for commandline-tools and platform-tools
core.addPath(`${cmdlineToolsPath}/latest:${cmdlineToolsPath}/latest/bin:${process.env.ANDROID_SDK_ROOT}/platform-tools`); core.addPath(`${cmdlineToolsPath}/latest:${cmdlineToolsPath}/latest/bin:${process.env.ANDROID_SDK_ROOT}/platform-tools`);
// set standard AVD path // set standard AVD path
core.exportVariable('ANDROID_AVD_HOME', `${process.env.HOME}/.android/avd`); core.exportVariable('ANDROID_AVD_HOME', `${process.env.HOME}/.android/avd`);
// accept all Android SDK licenses // accept all Android SDK licenses
await exec.exec(`sh -c \\"yes | sdkmanager --licenses > /dev/null"`); await exec.exec(`sh -c \\"yes | sdkmanager --licenses > /dev/null"`);
console.log('Installing latest build tools, platform tools, and platform.'); console.log('Installing latest build tools, platform tools, and platform.');
await exec.exec(`sh -c \\"sdkmanager --install 'build-tools;${BUILD_TOOLS_VERSION}' platform-tools 'platforms;android-${apiLevel}' > /dev/null"`); 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"`);
if (emulatorBuild) { if (emulatorBuild) {
console.log(`Installing emulator build ${emulatorBuild}.`); console.log(`Installing emulator build ${emulatorBuild}.`);
// TODO find out the correct download URLs for all build ids // TODO find out the correct download URLs for all build ids
const downloadUrlSuffix = Number(emulatorBuild.charAt(0)) > 6 ? `_x64-${emulatorBuild}` : `-${emulatorBuild}`; const downloadUrlSuffix = Number(emulatorBuild.charAt(0)) > 6 ? `_x64-${emulatorBuild}` : `-${emulatorBuild}`;
await exec.exec(`curl -fo emulator.zip https://dl.google.com/android/repository/emulator-${isOnMac ? 'darwin' : 'linux'}${downloadUrlSuffix}.zip`); await exec.exec(`curl -fo emulator.zip https://dl.google.com/android/repository/emulator-${isOnMac ? 'darwin' : 'linux'}${downloadUrlSuffix}.zip`);
await exec.exec(`unzip -o -q emulator.zip -d ${process.env.ANDROID_SDK_ROOT}`); await exec.exec(`unzip -o -q emulator.zip -d ${process.env.ANDROID_SDK_ROOT}`);
await io.rmRF('emulator.zip'); await io.rmRF('emulator.zip');
} }
console.log('Installing system images.'); console.log('Installing system images.');
await exec.exec(`sh -c \\"sdkmanager --install 'system-images;android-${apiLevel};${target};${arch}' --channel=${channelId} > /dev/null"`); await exec.exec(`sh -c \\"sdkmanager --install 'system-images;android-${apiLevel};${target};${arch}' --channel=${channelId} > /dev/null"`);
if (ndkVersion) { if (ndkVersion) {
console.log(`Installing NDK ${ndkVersion}.`); console.log(`Installing NDK ${ndkVersion}.`);
await exec.exec(`sh -c \\"sdkmanager --install 'ndk;${ndkVersion}' --channel=${channelId} > /dev/null"`); await exec.exec(`sh -c \\"sdkmanager --install 'ndk;${ndkVersion}' --channel=${channelId} > /dev/null"`);
} }
if (cmakeVersion) { if (cmakeVersion) {
console.log(`Installing CMake ${cmakeVersion}.`); console.log(`Installing CMake ${cmakeVersion}.`);
await exec.exec(`sh -c \\"sdkmanager --install 'cmake;${cmakeVersion}' --channel=${channelId} > /dev/null"`); await exec.exec(`sh -c \\"sdkmanager --install 'cmake;${cmakeVersion}' --channel=${channelId} > /dev/null"`);
}
} finally {
console.log(`::endgroup::`);
} }
} }
+3 -3
View File
@@ -2,13 +2,13 @@ apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
android { android {
compileSdkVersion 31 compileSdkVersion 32
buildToolsVersion "31.0.0" buildToolsVersion "32.0.0"
defaultConfig { defaultConfig {
applicationId "com.example.testapp" applicationId "com.example.testapp"
minSdkVersion 15 minSdkVersion 15
targetSdkVersion 31 targetSdkVersion 32
versionCode 1 versionCode 1
versionName "1.0" versionName "1.0"
+2 -2
View File
@@ -1,13 +1,13 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
ext.kotlin_version = '1.5.30' ext.kotlin_version = '1.6.10'
repositories { repositories {
google() google()
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:7.0.1' classpath 'com.android.tools.build:gradle:7.1.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
+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-7.3-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-bin.zip