Compare commits

...

36 Commits

Author SHA1 Message Date
Yang 2f678a7026 Merge branch 'master' into release/v2
* master:
  Prepare for release 2.7.0.
  Security audit.
  Support specifying versions of NDK and CMake to install.
2020-04-11 21:52:57 +10:00
Yang 33176ea112 Prepare for release 2.7.0. 2020-04-11 21:50:47 +10:00
Yang 8972f964c9 Security audit. 2020-04-11 21:48:08 +10:00
Yang 438dce7110 Support specifying versions of NDK and CMake to install. 2020-04-11 21:02:42 +10:00
Yang Chen e4eb880655 Merge branch 'master' into release/v2
* master:
  Prepare for release 2.6.2.
  Add gradle caching.
  Use correct cli-tools binary for mac.
  Remove API 28 from workflow.
2020-04-03 00:13:02 +11:00
Yang Chen 61dd774a5b Prepare for release 2.6.2. 2020-04-03 00:12:04 +11:00
Yang Chen 8c62a78107 Add gradle caching. 2020-04-02 23:43:06 +11:00
Yang Chen 0d53ec8e43 Use correct cli-tools binary for mac. 2020-04-02 22:56:56 +11:00
Yang Chen 730757f124 Remove API 28 from workflow. 2020-04-01 09:37:55 +11:00
Yang Chen a3b0e6d6d8 Update node_modules. 2020-04-01 00:56:37 +11:00
Yang Chen 0159db621d Merge branch 'master' into release/v2
* master:
  Prepare for release 2.6.1.
  Fix sdk license issue with API 28+ system images on Linux.
2020-04-01 00:54:08 +11:00
Yang Chen 22965df722 Prepare for release 2.6.1. 2020-04-01 00:51:14 +11:00
Yang Chen 49a3d042e9 Fix sdk license issue with API 28+ system images on Linux. 2020-04-01 00:38:58 +11:00
Yang Chen 769ff281b3 Merge branch 'master' into release/v2
* master:
  Prepare for release 2.6.0.
  Update packages.
  Add information re. hardware acceleration for linux support.
  Support running on linux without hardware accleration.
  Gradle 6.3.
  Gradle 6.3-rc-4.
  Update workflow to use Java 14.
  Update npm packages.
  Bump acorn from 5.7.3 to 5.7.4
2020-03-28 15:00:56 +11:00
Yang Chen 0d966550cf Prepare for release 2.6.0. 2020-03-28 14:59:20 +11:00
Yang Chen eb9e069f3f Update packages. 2020-03-28 14:10:50 +11:00
Yang Chen 0ac3f34493 Add information re. hardware acceleration for linux support. 2020-03-28 14:10:50 +11:00
Yang Chen 345ace6178 Support running on linux without hardware accleration. 2020-03-28 14:10:50 +11:00
Yang Chen 490f28e17c Gradle 6.3. 2020-03-25 23:23:07 +11:00
Yang Chen fc08a40bf0 Gradle 6.3-rc-4. 2020-03-25 23:23:07 +11:00
Yang Chen 75464a704b Update workflow to use Java 14. 2020-03-25 23:23:07 +11:00
Yang Chen de92fdaa03 Update npm packages. 2020-03-17 21:16:57 +11:00
dependabot[bot] 47063ae93b Bump acorn from 5.7.3 to 5.7.4
Bumps [acorn](https://github.com/acornjs/acorn) from 5.7.3 to 5.7.4.
- [Release notes](https://github.com/acornjs/acorn/releases)
- [Commits](https://github.com/acornjs/acorn/compare/5.7.3...5.7.4)

Signed-off-by: dependabot[bot] <support@github.com>
2020-03-17 20:55:34 +11:00
Yang Chen 6bb3965c96 Merge branch 'master' into release/v2
* master:
  Prepare for release 2.5.0.
  Add support for API 15-19 system images.
  Use new cmdline-tools with support for running sdkmanager and avdmanager with Java 8+.
  Update test-fixture dependencies. Drop minSdkVersion to 15.
  Update to AGP 3.6.0 and Gradle 6.2.1 for test-fixture project.
2020-03-05 14:55:36 +11:00
Yang Chen da846ad45b Prepare for release 2.5.0. 2020-03-05 14:54:33 +11:00
Yang Chen ede514c8c2 Add support for API 15-19 system images. 2020-03-05 14:50:59 +11:00
Yang Chen 4a949058e2 Use new cmdline-tools with support for running sdkmanager and avdmanager with Java 8+. 2020-03-05 14:34:19 +11:00
Yang Chen e1015a487b Update test-fixture dependencies. Drop minSdkVersion to 15. 2020-03-05 14:34:19 +11:00
Yang Chen 883b8114b1 Update to AGP 3.6.0 and Gradle 6.2.1 for test-fixture project. 2020-02-26 18:33:53 +11:00
Yang Chen 2754675516 Merge branch 'master' into release/v2
* master:
  Prepare for release 2.4.0.
  Add support for setting a custom working-directory.
  Fix typo
2020-02-15 18:52:13 +11:00
Yang Chen aaa1121ed1 Prepare for release 2.4.0. 2020-02-15 18:51:35 +11:00
Yang Chen ff311e8a7d Add support for setting a custom working-directory. 2020-02-15 18:29:17 +11:00
Ricky Ng-Adam d0fa6c63f8 Fix typo 2020-02-15 18:08:40 +11:00
Yang Chen bdf1f83901 Merge branch 'master' into release/v2
* master:
  Prepare for release 2.3.2.
  Fix escaped environment variables in script.
2020-02-04 12:28:52 +11:00
Yang Chen d3a226f97f Prepare for release 2.3.2. 2020-02-04 12:27:56 +11:00
Yang Chen 4082c705ad Fix escaped environment variables in script. 2020-02-04 12:25:35 +11:00
41 changed files with 4293 additions and 2226 deletions
+22 -10
View File
@@ -9,11 +9,17 @@ on:
jobs:
test:
runs-on: macOS-latest
timeout-minutes: 10
runs-on: ${{ matrix.os }}
timeout-minutes: 15
strategy:
matrix:
api-level: [21, 23, 29]
os: [macos-latest, ubuntu-latest]
api-level: [16, 23, 29]
exclude:
- os: ubuntu-latest
api-level: 23
- os: ubuntu-latest
api-level: 29
steps:
- name: checkout
uses: actions/checkout@v2
@@ -28,23 +34,29 @@ jobs:
npm run lint
npm test
- name: Prepare test fixture
run: cp -a ./test-fixture/* ./
- name: Java 13
- name: Java 14
uses: actions/setup-java@v1
with:
java-version: 13
java-version: 14
- name: Clean cache
if: matrix.os == 'macos-latest'
run: mv ~/.gradle/caches ~/.gradle/.invalid_caches
- uses: actions/cache@v1
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }}
- name: run action
uses: ./
with:
api-level: ${{ matrix.api-level }}
target: google_apis
target: default
arch: x86
profile: Nexus 6
emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim -camera-back none
disable-animations: true
working-directory: ./test-fixture/
script: |
echo $GITHUB_REPOSITORY
adb devices
./gradlew help
./gradlew connectedDebugAndroidTest
+29
View File
@@ -1,5 +1,34 @@
# Change Log
## v2.7.0
* Added support for specifying versions of **NDK** and **CMake** to install.
## v2.6.2
* Fixed an issue where the Linux command-line tools binary is used for `macos`.
## v2.6.1
* Fixed SDK license issue on Linux when downloading API 28+ system images - [#42](https://github.com/ReactiveCircus/android-emulator-runner/issues/42).
## v2.6.0
* Added support for Linux VMs (no hardware acceleration) - [#15](https://github.com/ReactiveCircus/android-emulator-runner/issues/15).
## v2.5.0
* Added support for API 15-19 system images - [#26](https://github.com/ReactiveCircus/android-emulator-runner/issues/26).
* Switched to the new SDK command-line tools which supports running `sdkmanager` and `avdmanager` with Java 9+ - [#25](https://github.com/ReactiveCircus/android-emulator-runner/issues/25).
## v2.4.0
* Added support for setting custom `working-directory` - e.g. `./android` if your root Gradle project is under the `./android` sub-directory within your repository - [#22](https://github.com/ReactiveCircus/android-emulator-runner/issues/22).
## v2.3.2
* Fixed an issue where environment variables are escaped in script - [#19](https://github.com/ReactiveCircus/android-emulator-runner/issues/19).
## v2.3.1
* Bumped Android Build tools to 29.0.3.
+32 -6
View File
@@ -4,7 +4,7 @@
<a href="https://github.com/ReactiveCircus/android-emulator-runner"><img alt="GitHub Actions status" src="https://github.com/ReactiveCircus/android-emulator-runner/workflows/Main%20workflow/badge.svg"></a>
</p>
A GitHub Action for installing, configuring and running Android Emulators on macOS virtual machines.
A GitHub Action for installing, configuring and running hardware-accelerated Android Emulators on macOS virtual machines (or Linux virtual machines but without hardware accleration).
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).
@@ -22,14 +22,16 @@ This action automates the process by doing the following:
## Usage
Note that this action must be run on a **macOS** VM, e.g. `macOS-latest` or `macOS-10.14`.
It is recommended to run this action on a **macOS** VM, e.g. `macos-latest` or `macos-10.15` to take advantage of hardware accleration support provided by **HAXM**.
Please note that while Linux VMs (e.g. `ubuntu-latest` or `ubuntu-18.04`) are also supported, hardware acceleration will **not** be available.
A workflow that uses **android-emulator-runner** to run your instrumented tests on **API 29**:
```
jobs:
test:
runs-on: macOS-latest
runs-on: macos-latest
steps:
- name: checkout
uses: actions/checkout@v2
@@ -46,7 +48,7 @@ We can also leverage GitHub Actions's build matrix to test across multiple confi
```
jobs:
test:
runs-on: macOS-latest
runs-on: macos-latest
strategy:
matrix:
api-level: [21, 23, 29]
@@ -65,17 +67,41 @@ jobs:
script: ./gradlew connectedCheck
```
If you need specific versions of **NDK** and **CMake** installed:
```
jobs:
test:
runs-on: macos-latest
steps:
- name: checkout
uses: actions/checkout@v2
- name: run tests
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 29
ndk: 21.0.6113669
cmake: 3.10.2.4988404
script: ./gradlew connectedCheck
```
## Configurations
| | **Required** | **Default** | **Description** |
|----------------------|--------------|-------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `api-level` | Required | N/A | API level of the platform system image - e.g. 23 for Android Marshmallow, 29 for Android 10. **Minimum API level supported is 21**. |
| `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` or `google_apis`. |
| `arch` | Optional | `x86` | CPU architecture of the system image - `x86` or `x86_64`. |
| `arch` | Optional | `x86` | CPU architecture of the system image - `x86` or `x86_64`. Note that `x86_64` image is only available for API 21+. |
| `profile` | Optional | N/A | Hardware profile used for creating the AVD - e.g. `Nexus 6`. For a list of all profiles available, run `$ANDROID_HOME/tools/bin/avdmanager list` and refer to the results under "Available Android Virtual Devices". |
| `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`. |
| `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` |
| `cmake` | Optional | N/A | Version of CMake to install - e.g. `3.10.2.4988404` |
| `script` | Required | N/A | Custom script to run - e.g. to run Android instrumented tests on the emulator: `./gradlew connectedCheck` |
Default `emulator-options`: `-no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim`.
_Please note that if you are running on a Linux VM, `-no-accel` will be added to the `emulator-options` to make sure hardware acceleration is turned off._
+2 -2
View File
@@ -17,14 +17,14 @@ describe('api-level validator tests', () => {
it('Throws if api-level is lower than min API supported', () => {
const func = () => {
validator.checkApiLevel('20');
validator.checkApiLevel('14');
};
expect(func).toThrowError(`Minimum API level supported is ${validator.MIN_API_LEVEL}.`);
});
it('Validates successfully with valid api-level', () => {
const func1 = () => {
validator.checkApiLevel('21');
validator.checkApiLevel('15');
};
expect(func1).not.toThrow();
+8 -2
View File
@@ -15,7 +15,7 @@ inputs:
description: 'CPU architecture of the system image - x86 or x86_64'
default: 'x86'
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`.'
emulator-options:
description: 'command-line options used when launching the emulator - e.g. `-no-window -no-snapshot -camera-back emulated`.'
default: '-no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim'
@@ -23,7 +23,13 @@ inputs:
description: 'whether to disable animations - true or false'
default: 'true'
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:
description: 'A custom working directory - e.g. `./android` if your root Gradle project is under the `./android` sub-directory within your repository.'
ndk:
description: 'version of NDK to install - e.g. 21.0.6113669'
cmake:
description: 'version of CMake to install - e.g. 3.10.2.4988404'
script:
description: 'custom script to run - e.g. `./gradlew connectedCheck`'
required: true
+14 -12
View File
@@ -16,9 +16,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
};
Object.defineProperty(exports, "__esModule", { value: true });
const exec = __importStar(require("@actions/exec"));
const EMULATOR_BOOT_TIMEOUT_SECONDS = 300;
const AVD_MANAGER_PATH = `${process.env.ANDROID_HOME}/tools/bin/avdmanager`;
const ADB_PATH = `${process.env.ANDROID_HOME}/platform-tools/adb`;
const EMULATOR_BOOT_TIMEOUT_SECONDS = 600;
/**
* Creates and launches a new AVD instance with the specified configurations.
*/
@@ -27,14 +25,18 @@ function launchEmulator(apiLevel, target, arch, profile, emulatorOptions, disabl
// create a new AVD
if (profile.trim() !== '') {
console.log(`Creating AVD with custom profile ${profile}`);
yield exec.exec(`${AVD_MANAGER_PATH} create avd --force -n test --abi "${target}/${arch}" --package "system-images;android-${apiLevel};${target};${arch}" --device "${profile}"`);
yield exec.exec(`avdmanager create avd --force -n test --abi "${target}/${arch}" --package "system-images;android-${apiLevel};${target};${arch}" --device "${profile}"`);
}
else {
console.log(`Creating AVD without custom profile.`);
yield exec.exec(`sh -c \\"echo no | ${AVD_MANAGER_PATH} create avd --force -n test --abi '${target}/${arch}' --package 'system-images;android-${apiLevel};${target};${arch}'"`);
yield exec.exec(`sh -c \\"echo no | avdmanager create avd --force -n test --abi '${target}/${arch}' --package 'system-images;android-${apiLevel};${target};${arch}'"`);
}
// start emulator
console.log('Starting emulator.');
// turn off hardware acceleration on Linux
if (process.platform === 'linux') {
emulatorOptions += ' -accel off';
}
yield exec.exec(`sh -c \\"${process.env.ANDROID_HOME}/emulator/emulator -avd test ${emulatorOptions} &"`, [], {
listeners: {
stderr: (data) => {
@@ -46,13 +48,13 @@ function launchEmulator(apiLevel, target, arch, profile, emulatorOptions, disabl
});
// wait for emulator to complete booting
yield waitForDevice();
yield exec.exec(`${ADB_PATH} shell input keyevent 82`);
yield exec.exec(`adb shell input keyevent 82`);
// disable animations
if (disableAnimations) {
console.log('Disabling animations.');
yield exec.exec(`${ADB_PATH} shell settings put global window_animation_scale 0.0`);
yield exec.exec(`${ADB_PATH} shell settings put global transition_animation_scale 0.0`);
yield exec.exec(`${ADB_PATH} shell settings put global animator_duration_scale 0.0`);
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`);
}
});
}
@@ -63,7 +65,7 @@ exports.launchEmulator = launchEmulator;
function killEmulator() {
return __awaiter(this, void 0, void 0, function* () {
try {
yield exec.exec(`${ADB_PATH} -s emulator-5554 emu kill`);
yield exec.exec(`adb -s emulator-5554 emu kill`);
}
catch (error) {
console.log(error.message);
@@ -83,7 +85,7 @@ function waitForDevice() {
while (!booted) {
try {
let result = '';
yield exec.exec(`${ADB_PATH} shell getprop sys.boot_completed`, [], {
yield exec.exec(`adb shell getprop sys.boot_completed`, [], {
listeners: {
stdout: (data) => {
result += data.toString();
@@ -97,7 +99,7 @@ function waitForDevice() {
}
}
catch (error) {
console.log(error.message);
console.warn(error.message);
}
if (attempts < maxAttemps) {
yield delay(retryInterval * 1000);
+1 -1
View File
@@ -1,6 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MIN_API_LEVEL = 21;
exports.MIN_API_LEVEL = 15;
exports.VALID_TARGETS = ['default', 'google_apis'];
exports.VALID_ARCHS = ['x86', 'x86_64'];
function checkApiLevel(apiLevel) {
-60
View File
@@ -1,60 +0,0 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const core = __importStar(require("@actions/core"));
const exec = __importStar(require("@actions/exec"));
/**
* Returns the current $JAVA_HOME path.
*/
function getCurrentJavaHome() {
return __awaiter(this, void 0, void 0, function* () {
let defaultJavaHome = '';
yield exec.exec(`sh -c \\"echo $JAVA_HOME"`, [], {
listeners: {
stdout: (data) => {
defaultJavaHome += data.toString();
}
}
});
return defaultJavaHome.trim();
});
}
exports.getCurrentJavaHome = getCurrentJavaHome;
/**
* Returns the Java 8 $JAVA_HOME path.
*/
function getJavaHomeV8() {
return __awaiter(this, void 0, void 0, function* () {
let javaHomeV8 = '';
yield exec.exec(`/usr/libexec/java_home -v 1.8`, [], {
listeners: {
stdout: (data) => {
javaHomeV8 += data.toString();
}
}
});
return javaHomeV8.trim();
});
}
exports.getJavaHomeV8 = getJavaHomeV8;
/**
* Sets $JAVA_HOME to the specified path.
*/
function setJavaHome(path) {
core.exportVariable('JAVA_HOME', path);
}
exports.setJavaHome = setJavaHome;
+33 -17
View File
@@ -21,13 +21,17 @@ const input_validator_1 = require("./input-validator");
const emulator_manager_1 = require("./emulator-manager");
const exec = __importStar(require("@actions/exec"));
const script_parser_1 = require("./script-parser");
const java_version_manager_1 = require("./java-version-manager");
function run() {
return __awaiter(this, void 0, void 0, function* () {
try {
// only support running on macOS
// only support running on macOS or Linux
if (process.platform !== 'darwin') {
throw new Error('This action is expected to be run within a macOS virtual machine to enable hardware acceleration.');
if (process.platform === 'linux') {
console.warn(`You're running a Linux VM where hardware acceleration is not available. Please consider using a macOS VM instead to take advantage of native hardware acceleration support provided by HAXM.`);
}
else {
throw new Error('Unsupported virtual machine: please use either macos or ubuntu VM.');
}
}
// API level of the platform and system image
const apiLevelInput = core.getInput('api-level', { required: true });
@@ -60,6 +64,24 @@ function run() {
console.log(`using emulator build: ${emulatorBuildInput}`);
}
const emulatorBuild = !emulatorBuildInput ? undefined : emulatorBuildInput;
// custom working directory
const workingDirectoryInput = core.getInput('working-directory');
if (workingDirectoryInput) {
console.log(`custom working directory: ${workingDirectoryInput}`);
}
const workingDirectory = !workingDirectoryInput ? undefined : workingDirectoryInput;
// version of NDK to install
const ndkInput = core.getInput('ndk');
if (ndkInput) {
console.log(`version of NDK to install: ${ndkInput}`);
}
const ndkVersion = !ndkInput ? undefined : ndkInput;
// version of CMake to install
const cmakeInput = core.getInput('cmake');
if (cmakeInput) {
console.log(`version of CMake to install: ${cmakeInput}`);
}
const cmakeVersion = !cmakeInput ? undefined : cmakeInput;
// custom script to run
const scriptInput = core.getInput('script', { required: true });
const scripts = script_parser_1.parseScript(scriptInput);
@@ -67,24 +89,18 @@ function run() {
scripts.forEach((script) => __awaiter(this, void 0, void 0, function* () {
console.log(`${script}`);
}));
// use Java 8 for sdkmanager and avdmanager
const defaultJavaHome = yield java_version_manager_1.getCurrentJavaHome();
java_version_manager_1.setJavaHome(yield java_version_manager_1.getJavaHomeV8());
// install SDK
yield sdk_installer_1.installAndroidSdk(apiLevel, target, arch, emulatorBuild);
try {
// launch an emulator
yield emulator_manager_1.launchEmulator(apiLevel, target, arch, profile, emulatorOptions, disableAnimations);
}
catch (error) {
core.setFailed(error.message);
}
// use default JAVA_HOME for running custom script
java_version_manager_1.setJavaHome(defaultJavaHome);
yield sdk_installer_1.installAndroidSdk(apiLevel, target, arch, emulatorBuild, ndkVersion, cmakeVersion);
// launch an emulator
yield emulator_manager_1.launchEmulator(apiLevel, target, arch, profile, emulatorOptions, disableAnimations);
// 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(`${script}`);
yield exec.exec(`sh -c \\"${script}"`);
}
}
catch (error) {
+33 -9
View File
@@ -15,30 +15,54 @@ var __importStar = (this && this.__importStar) || function (mod) {
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const core = __importStar(require("@actions/core"));
const exec = __importStar(require("@actions/exec"));
const BUILD_TOOLS_VERSION = '29.0.3';
const CMDLINE_TOOLS_URL_MAC = 'https://dl.google.com/android/repository/commandlinetools-mac-6200805_latest.zip';
const CMDLINE_TOOLS_URL_LINUX = 'https://dl.google.com/android/repository/commandlinetools-linux-6200805_latest.zip';
/**
* Installs & updates the Android SDK for the macOS platform, including SDK platform for the chosen API level, latest build tools, platform tools, Android Emulator,
* and the system image for the chosen API level, CPU arch, and target.
*/
function installAndroidSdk(apiLevel, target, arch, emulatorBuild) {
function installAndroidSdk(apiLevel, target, arch, emulatorBuild, ndkVersion, cmakeVersion) {
return __awaiter(this, void 0, void 0, function* () {
const sdkmangerPath = `${process.env.ANDROID_HOME}/tools/bin/sdkmanager`;
const isOnMac = process.platform === 'darwin';
console.log('Installing new cmdline-tools.');
const sdkUrl = isOnMac ? CMDLINE_TOOLS_URL_MAC : CMDLINE_TOOLS_URL_LINUX;
yield exec.exec(`sudo mkdir ${process.env.ANDROID_HOME}/cmdline-tools`);
yield exec.exec(`curl -fo commandlinetools.zip ${sdkUrl}`);
yield exec.exec(`sudo unzip -q commandlinetools.zip -d ${process.env.ANDROID_HOME}/cmdline-tools`);
yield exec.exec(`sudo rm -f commandlinetools.zip`);
// add paths for commandline-tools and platform-tools
core.addPath(`${process.env.ANDROID_HOME}/cmdline-tools/tools:${process.env.ANDROID_HOME}/cmdline-tools/tools/bin:${process.env.ANDROID_HOME}/platform-tools`);
// additional permission and license requirements for Linux
if (!isOnMac) {
yield exec.exec(`sh -c \\"sudo chmod -R 777 ${process.env.ANDROID_HOME}"`);
yield exec.exec(`sh -c \\"echo -e '\n84831b9409646a918e30573bab4c9c91346d8abd' > ${process.env.ANDROID_HOME}/licenses/android-sdk-preview-license"`);
}
console.log('Installing latest build tools, platform tools, and platform.');
yield exec.exec(`sh -c \\"${sdkmangerPath} --install 'build-tools;${BUILD_TOOLS_VERSION}' platform-tools 'platforms;android-${apiLevel}' > /dev/null"`);
yield exec.exec(`sh -c \\"sdkmanager --install 'build-tools;${BUILD_TOOLS_VERSION}' platform-tools 'platforms;android-${apiLevel}' > /dev/null"`);
if (emulatorBuild) {
console.log(`Installing emulator build ${emulatorBuild}.`);
yield exec.exec(`curl -fo emulator.zip https://dl.google.com/android/repository/emulator-darwin-${emulatorBuild}.zip`);
yield exec.exec(`rm -rf ${process.env.ANDROID_HOME}/emulator`);
yield exec.exec(`unzip -q emulator.zip -d ${process.env.ANDROID_HOME}`);
yield exec.exec(`rm -f emulator.zip`);
yield exec.exec(`curl -fo emulator.zip https://dl.google.com/android/repository/emulator-${isOnMac ? 'darwin' : 'linux'}-${emulatorBuild}.zip`);
yield exec.exec(`sudo rm -rf ${process.env.ANDROID_HOME}/emulator`);
yield exec.exec(`sudo unzip -q emulator.zip -d ${process.env.ANDROID_HOME}`);
yield exec.exec(`sudo rm -f emulator.zip`);
}
else {
console.log('Installing latest emulator.');
yield exec.exec(`sh -c \\"${sdkmangerPath} --install emulator > /dev/null"`);
yield exec.exec(`sh -c \\"sdkmanager --install emulator > /dev/null"`);
}
console.log('Installing system images.');
yield exec.exec(`sh -c \\"${sdkmangerPath} --install 'system-images;android-${apiLevel};${target};${arch}' > /dev/null"`);
yield exec.exec(`sh -c \\"sdkmanager --install 'system-images;android-${apiLevel};${target};${arch}' > /dev/null"`);
if (ndkVersion) {
console.log(`Installing NDK ${ndkVersion}.`);
yield exec.exec(`sh -c \\"sdkmanager --install 'ndk;${ndkVersion}' > /dev/null"`);
}
if (cmakeVersion) {
console.log(`Installing CMake ${cmakeVersion}.`);
yield exec.exec(`sh -c \\"sdkmanager --install 'cmake;${cmakeVersion}' > /dev/null"`);
}
});
}
exports.installAndroidSdk = installAndroidSdk;
+8
View File
@@ -0,0 +1,8 @@
language: node_js
node_js:
- "0.8"
- "0.10"
- "0.12"
- "iojs"
before_install:
- npm install -g npm@~1.4.6
+18
View File
@@ -0,0 +1,18 @@
This software is released under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+2
View File
@@ -0,0 +1,2 @@
var argv = require('../')(process.argv.slice(2));
console.log(argv);
+245
View File
@@ -0,0 +1,245 @@
module.exports = function (args, opts) {
if (!opts) opts = {};
var flags = { bools : {}, strings : {}, unknownFn: null };
if (typeof opts['unknown'] === 'function') {
flags.unknownFn = opts['unknown'];
}
if (typeof opts['boolean'] === 'boolean' && opts['boolean']) {
flags.allBools = true;
} else {
[].concat(opts['boolean']).filter(Boolean).forEach(function (key) {
flags.bools[key] = true;
});
}
var aliases = {};
Object.keys(opts.alias || {}).forEach(function (key) {
aliases[key] = [].concat(opts.alias[key]);
aliases[key].forEach(function (x) {
aliases[x] = [key].concat(aliases[key].filter(function (y) {
return x !== y;
}));
});
});
[].concat(opts.string).filter(Boolean).forEach(function (key) {
flags.strings[key] = true;
if (aliases[key]) {
flags.strings[aliases[key]] = true;
}
});
var defaults = opts['default'] || {};
var argv = { _ : [] };
Object.keys(flags.bools).forEach(function (key) {
setArg(key, defaults[key] === undefined ? false : defaults[key]);
});
var notFlags = [];
if (args.indexOf('--') !== -1) {
notFlags = args.slice(args.indexOf('--')+1);
args = args.slice(0, args.indexOf('--'));
}
function argDefined(key, arg) {
return (flags.allBools && /^--[^=]+$/.test(arg)) ||
flags.strings[key] || flags.bools[key] || aliases[key];
}
function setArg (key, val, arg) {
if (arg && flags.unknownFn && !argDefined(key, arg)) {
if (flags.unknownFn(arg) === false) return;
}
var value = !flags.strings[key] && isNumber(val)
? Number(val) : val
;
setKey(argv, key.split('.'), value);
(aliases[key] || []).forEach(function (x) {
setKey(argv, x.split('.'), value);
});
}
function setKey (obj, keys, value) {
var o = obj;
for (var i = 0; i < keys.length-1; i++) {
var key = keys[i];
if (key === '__proto__') return;
if (o[key] === undefined) o[key] = {};
if (o[key] === Object.prototype || o[key] === Number.prototype
|| o[key] === String.prototype) o[key] = {};
if (o[key] === Array.prototype) o[key] = [];
o = o[key];
}
var key = keys[keys.length - 1];
if (key === '__proto__') return;
if (o === Object.prototype || o === Number.prototype
|| o === String.prototype) o = {};
if (o === Array.prototype) o = [];
if (o[key] === undefined || flags.bools[key] || typeof o[key] === 'boolean') {
o[key] = value;
}
else if (Array.isArray(o[key])) {
o[key].push(value);
}
else {
o[key] = [ o[key], value ];
}
}
function aliasIsBoolean(key) {
return aliases[key].some(function (x) {
return flags.bools[x];
});
}
for (var i = 0; i < args.length; i++) {
var arg = args[i];
if (/^--.+=/.test(arg)) {
// Using [\s\S] instead of . because js doesn't support the
// 'dotall' regex modifier. See:
// http://stackoverflow.com/a/1068308/13216
var m = arg.match(/^--([^=]+)=([\s\S]*)$/);
var key = m[1];
var value = m[2];
if (flags.bools[key]) {
value = value !== 'false';
}
setArg(key, value, arg);
}
else if (/^--no-.+/.test(arg)) {
var key = arg.match(/^--no-(.+)/)[1];
setArg(key, false, arg);
}
else if (/^--.+/.test(arg)) {
var key = arg.match(/^--(.+)/)[1];
var next = args[i + 1];
if (next !== undefined && !/^-/.test(next)
&& !flags.bools[key]
&& !flags.allBools
&& (aliases[key] ? !aliasIsBoolean(key) : true)) {
setArg(key, next, arg);
i++;
}
else if (/^(true|false)$/.test(next)) {
setArg(key, next === 'true', arg);
i++;
}
else {
setArg(key, flags.strings[key] ? '' : true, arg);
}
}
else if (/^-[^-]+/.test(arg)) {
var letters = arg.slice(1,-1).split('');
var broken = false;
for (var j = 0; j < letters.length; j++) {
var next = arg.slice(j+2);
if (next === '-') {
setArg(letters[j], next, arg)
continue;
}
if (/[A-Za-z]/.test(letters[j]) && /=/.test(next)) {
setArg(letters[j], next.split('=')[1], arg);
broken = true;
break;
}
if (/[A-Za-z]/.test(letters[j])
&& /-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) {
setArg(letters[j], next, arg);
broken = true;
break;
}
if (letters[j+1] && letters[j+1].match(/\W/)) {
setArg(letters[j], arg.slice(j+2), arg);
broken = true;
break;
}
else {
setArg(letters[j], flags.strings[letters[j]] ? '' : true, arg);
}
}
var key = arg.slice(-1)[0];
if (!broken && key !== '-') {
if (args[i+1] && !/^(-|--)[^-]/.test(args[i+1])
&& !flags.bools[key]
&& (aliases[key] ? !aliasIsBoolean(key) : true)) {
setArg(key, args[i+1], arg);
i++;
}
else if (args[i+1] && /^(true|false)$/.test(args[i+1])) {
setArg(key, args[i+1] === 'true', arg);
i++;
}
else {
setArg(key, flags.strings[key] ? '' : true, arg);
}
}
}
else {
if (!flags.unknownFn || flags.unknownFn(arg) !== false) {
argv._.push(
flags.strings['_'] || !isNumber(arg) ? arg : Number(arg)
);
}
if (opts.stopEarly) {
argv._.push.apply(argv._, args.slice(i + 1));
break;
}
}
}
Object.keys(defaults).forEach(function (key) {
if (!hasKey(argv, key.split('.'))) {
setKey(argv, key.split('.'), defaults[key]);
(aliases[key] || []).forEach(function (x) {
setKey(argv, x.split('.'), defaults[key]);
});
}
});
if (opts['--']) {
argv['--'] = new Array();
notFlags.forEach(function(key) {
argv['--'].push(key);
});
}
else {
notFlags.forEach(function(key) {
argv._.push(key);
});
}
return argv;
};
function hasKey (obj, keys) {
var o = obj;
keys.slice(0,-1).forEach(function (key) {
o = (o[key] || {});
});
var key = keys[keys.length - 1];
return key in o;
}
function isNumber (x) {
if (typeof x === 'number') return true;
if (/^0x[0-9a-f]+$/i.test(x)) return true;
return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x);
}
+80
View File
@@ -0,0 +1,80 @@
{
"_args": [
[
"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",
"devDependencies": {
"covert": "^1.0.0",
"tap": "~0.4.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": {
"coverage": "covert test/*.js",
"test": "tap test/*.js"
},
"testling": {
"files": "test/*.js",
"browsers": [
"ie/6..latest",
"ff/5",
"firefox/latest",
"chrome/10",
"chrome/latest",
"safari/5.1",
"safari/latest",
"opera/12"
]
},
"version": "1.2.5"
}
+95
View File
@@ -0,0 +1,95 @@
# minimist
parse argument options
This module is the guts of optimist's argument parser without all the
fanciful decoration.
# example
``` js
var argv = require('minimist')(process.argv.slice(2));
console.log(argv);
```
```
$ node example/parse.js -a beep -b boop
{ _: [], a: 'beep', b: 'boop' }
```
```
$ node example/parse.js -x 3 -y 4 -n5 -abc --beep=boop foo bar baz
{ _: [ 'foo', 'bar', 'baz' ],
x: 3,
y: 4,
n: 5,
a: true,
b: true,
c: true,
beep: 'boop' }
```
# security
Previous versions had a prototype pollution bug that could cause privilege
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
# methods
``` js
var parseArgs = require('minimist')
```
## var argv = parseArgs(args, opts={})
Return an argument object `argv` populated with the array arguments from `args`.
`argv._` contains all the arguments that didn't have an option associated with
them.
Numeric-looking arguments will be returned as numbers unless `opts.string` or
`opts.boolean` is set for that argument name.
Any arguments after `'--'` will not be parsed and will end up in `argv._`.
options can be:
* `opts.string` - a string or array of strings argument names to always treat as
strings
* `opts.boolean` - a boolean, string or array of strings to always treat as
booleans. if `true` will treat all double hyphenated arguments without equal signs
as boolean (e.g. affects `--foo`, not `-f` or `--foo=bar`)
* `opts.alias` - an object mapping string names to strings or arrays of string
argument names to use as aliases
* `opts.default` - an object mapping string argument names to default values
* `opts.stopEarly` - when true, populate `argv._` with everything after the
first non-option
* `opts['--']` - when true, populate `argv._` with everything before the `--`
and `argv['--']` with everything after the `--`. Here's an example:
```
> require('./')('one two three -- four five --six'.split(' '), { '--': true })
{ _: [ 'one', 'two', 'three' ],
'--': [ 'four', 'five', '--six' ] }
```
Note that with `opts['--']` set, parsing for arguments still stops after the
`--`.
* `opts.unknown` - a function which is invoked with a command line parameter not
defined in the `opts` configuration object. If the function returns `false`, the
unknown option is not added to `argv`.
# install
With [npm](https://npmjs.org) do:
```
npm install minimist
```
# license
MIT
+32
View File
@@ -0,0 +1,32 @@
var parse = require('../');
var test = require('tape');
test('flag boolean true (default all --args to boolean)', function (t) {
var argv = parse(['moo', '--honk', 'cow'], {
boolean: true
});
t.deepEqual(argv, {
honk: true,
_: ['moo', 'cow']
});
t.deepEqual(typeof argv.honk, 'boolean');
t.end();
});
test('flag boolean true only affects double hyphen arguments without equals signs', function (t) {
var argv = parse(['moo', '--honk', 'cow', '-p', '55', '--tacos=good'], {
boolean: true
});
t.deepEqual(argv, {
honk: true,
tacos: 'good',
p: 55,
_: ['moo', 'cow']
});
t.deepEqual(typeof argv.honk, 'boolean');
t.end();
});
+178
View File
@@ -0,0 +1,178 @@
var parse = require('../');
var test = require('tape');
test('flag boolean default false', function (t) {
var argv = parse(['moo'], {
boolean: ['t', 'verbose'],
default: { verbose: false, t: false }
});
t.deepEqual(argv, {
verbose: false,
t: false,
_: ['moo']
});
t.deepEqual(typeof argv.verbose, 'boolean');
t.deepEqual(typeof argv.t, 'boolean');
t.end();
});
test('boolean groups', function (t) {
var argv = parse([ '-x', '-z', 'one', 'two', 'three' ], {
boolean: ['x','y','z']
});
t.deepEqual(argv, {
x : true,
y : false,
z : true,
_ : [ 'one', 'two', 'three' ]
});
t.deepEqual(typeof argv.x, 'boolean');
t.deepEqual(typeof argv.y, 'boolean');
t.deepEqual(typeof argv.z, 'boolean');
t.end();
});
test('boolean and alias with chainable api', function (t) {
var aliased = [ '-h', 'derp' ];
var regular = [ '--herp', 'derp' ];
var opts = {
herp: { alias: 'h', boolean: true }
};
var aliasedArgv = parse(aliased, {
boolean: 'herp',
alias: { h: 'herp' }
});
var propertyArgv = parse(regular, {
boolean: 'herp',
alias: { h: 'herp' }
});
var expected = {
herp: true,
h: true,
'_': [ 'derp' ]
};
t.same(aliasedArgv, expected);
t.same(propertyArgv, expected);
t.end();
});
test('boolean and alias with options hash', function (t) {
var aliased = [ '-h', 'derp' ];
var regular = [ '--herp', 'derp' ];
var opts = {
alias: { 'h': 'herp' },
boolean: 'herp'
};
var aliasedArgv = parse(aliased, opts);
var propertyArgv = parse(regular, opts);
var expected = {
herp: true,
h: true,
'_': [ 'derp' ]
};
t.same(aliasedArgv, expected);
t.same(propertyArgv, expected);
t.end();
});
test('boolean and alias array with options hash', function (t) {
var aliased = [ '-h', 'derp' ];
var regular = [ '--herp', 'derp' ];
var alt = [ '--harp', 'derp' ];
var opts = {
alias: { 'h': ['herp', 'harp'] },
boolean: 'h'
};
var aliasedArgv = parse(aliased, opts);
var propertyArgv = parse(regular, opts);
var altPropertyArgv = parse(alt, opts);
var expected = {
harp: true,
herp: true,
h: true,
'_': [ 'derp' ]
};
t.same(aliasedArgv, expected);
t.same(propertyArgv, expected);
t.same(altPropertyArgv, expected);
t.end();
});
test('boolean and alias using explicit true', function (t) {
var aliased = [ '-h', 'true' ];
var regular = [ '--herp', 'true' ];
var opts = {
alias: { h: 'herp' },
boolean: 'h'
};
var aliasedArgv = parse(aliased, opts);
var propertyArgv = parse(regular, opts);
var expected = {
herp: true,
h: true,
'_': [ ]
};
t.same(aliasedArgv, expected);
t.same(propertyArgv, expected);
t.end();
});
// regression, see https://github.com/substack/node-optimist/issues/71
test('boolean and --x=true', function(t) {
var parsed = parse(['--boool', '--other=true'], {
boolean: 'boool'
});
t.same(parsed.boool, true);
t.same(parsed.other, 'true');
parsed = parse(['--boool', '--other=false'], {
boolean: 'boool'
});
t.same(parsed.boool, true);
t.same(parsed.other, 'false');
t.end();
});
test('boolean --boool=true', function (t) {
var parsed = parse(['--boool=true'], {
default: {
boool: false
},
boolean: ['boool']
});
t.same(parsed.boool, true);
t.end();
});
test('boolean --boool=false', function (t) {
var parsed = parse(['--boool=false'], {
default: {
boool: true
},
boolean: ['boool']
});
t.same(parsed.boool, false);
t.end();
});
test('boolean using something similar to true', function (t) {
var opts = { boolean: 'h' };
var result = parse(['-h', 'true.txt'], opts);
var expected = {
h: true,
'_': ['true.txt']
};
t.same(result, expected);
t.end();
});
+31
View File
@@ -0,0 +1,31 @@
var parse = require('../');
var test = require('tape');
test('-', function (t) {
t.plan(5);
t.deepEqual(parse([ '-n', '-' ]), { n: '-', _: [] });
t.deepEqual(parse([ '-' ]), { _: [ '-' ] });
t.deepEqual(parse([ '-f-' ]), { f: '-', _: [] });
t.deepEqual(
parse([ '-b', '-' ], { boolean: 'b' }),
{ b: true, _: [ '-' ] }
);
t.deepEqual(
parse([ '-s', '-' ], { string: 's' }),
{ s: '-', _: [] }
);
});
test('-a -- b', function (t) {
t.plan(3);
t.deepEqual(parse([ '-a', '--', 'b' ]), { a: true, _: [ 'b' ] });
t.deepEqual(parse([ '--a', '--', 'b' ]), { a: true, _: [ 'b' ] });
t.deepEqual(parse([ '--a', '--', 'b' ]), { a: true, _: [ 'b' ] });
});
test('move arguments after the -- into their own `--` array', function(t) {
t.plan(1);
t.deepEqual(
parse([ '--name', 'John', 'before', '--', 'after' ], { '--': true }),
{ name: 'John', _: [ 'before' ], '--': [ 'after' ] });
});
+35
View File
@@ -0,0 +1,35 @@
var test = require('tape');
var parse = require('../');
test('boolean default true', function (t) {
var argv = parse([], {
boolean: 'sometrue',
default: { sometrue: true }
});
t.equal(argv.sometrue, true);
t.end();
});
test('boolean default false', function (t) {
var argv = parse([], {
boolean: 'somefalse',
default: { somefalse: false }
});
t.equal(argv.somefalse, false);
t.end();
});
test('boolean default to null', function (t) {
var argv = parse([], {
boolean: 'maybe',
default: { maybe: null }
});
t.equal(argv.maybe, null);
var argv = parse(['--maybe'], {
boolean: 'maybe',
default: { maybe: null }
});
t.equal(argv.maybe, true);
t.end();
})
+22
View File
@@ -0,0 +1,22 @@
var parse = require('../');
var test = require('tape');
test('dotted alias', function (t) {
var argv = parse(['--a.b', '22'], {default: {'a.b': 11}, alias: {'a.b': 'aa.bb'}});
t.equal(argv.a.b, 22);
t.equal(argv.aa.bb, 22);
t.end();
});
test('dotted default', function (t) {
var argv = parse('', {default: {'a.b': 11}, alias: {'a.b': 'aa.bb'}});
t.equal(argv.a.b, 11);
t.equal(argv.aa.bb, 11);
t.end();
});
test('dotted default with no alias', function (t) {
var argv = parse('', {default: {'a.b': 11}});
t.equal(argv.a.b, 11);
t.end();
});
+16
View File
@@ -0,0 +1,16 @@
var parse = require('../');
var test = require('tape');
test('short -k=v' , function (t) {
t.plan(1);
var argv = parse([ '-b=123' ]);
t.deepEqual(argv, { b: 123, _: [] });
});
test('multi short -k=v' , function (t) {
t.plan(1);
var argv = parse([ '-a=whatever', '-b=robots' ]);
t.deepEqual(argv, { a: 'whatever', b: 'robots', _: [] });
});
+31
View File
@@ -0,0 +1,31 @@
var test = require('tape');
var parse = require('../');
test('long opts', function (t) {
t.deepEqual(
parse([ '--bool' ]),
{ bool : true, _ : [] },
'long boolean'
);
t.deepEqual(
parse([ '--pow', 'xixxle' ]),
{ pow : 'xixxle', _ : [] },
'long capture sp'
);
t.deepEqual(
parse([ '--pow=xixxle' ]),
{ pow : 'xixxle', _ : [] },
'long capture eq'
);
t.deepEqual(
parse([ '--host', 'localhost', '--port', '555' ]),
{ host : 'localhost', port : 555, _ : [] },
'long captures sp'
);
t.deepEqual(
parse([ '--host=localhost', '--port=555' ]),
{ host : 'localhost', port : 555, _ : [] },
'long captures eq'
);
t.end();
});
+36
View File
@@ -0,0 +1,36 @@
var parse = require('../');
var test = require('tape');
test('nums', function (t) {
var argv = parse([
'-x', '1234',
'-y', '5.67',
'-z', '1e7',
'-w', '10f',
'--hex', '0xdeadbeef',
'789'
]);
t.deepEqual(argv, {
x : 1234,
y : 5.67,
z : 1e7,
w : '10f',
hex : 0xdeadbeef,
_ : [ 789 ]
});
t.deepEqual(typeof argv.x, 'number');
t.deepEqual(typeof argv.y, 'number');
t.deepEqual(typeof argv.z, 'number');
t.deepEqual(typeof argv.w, 'string');
t.deepEqual(typeof argv.hex, 'number');
t.deepEqual(typeof argv._[0], 'number');
t.end();
});
test('already a number', function (t) {
var argv = parse([ '-x', 1234, 789 ]);
t.deepEqual(argv, { x : 1234, _ : [ 789 ] });
t.deepEqual(typeof argv.x, 'number');
t.deepEqual(typeof argv._[0], 'number');
t.end();
});
+197
View File
@@ -0,0 +1,197 @@
var parse = require('../');
var test = require('tape');
test('parse args', function (t) {
t.deepEqual(
parse([ '--no-moo' ]),
{ moo : false, _ : [] },
'no'
);
t.deepEqual(
parse([ '-v', 'a', '-v', 'b', '-v', 'c' ]),
{ v : ['a','b','c'], _ : [] },
'multi'
);
t.end();
});
test('comprehensive', function (t) {
t.deepEqual(
parse([
'--name=meowmers', 'bare', '-cats', 'woo',
'-h', 'awesome', '--multi=quux',
'--key', 'value',
'-b', '--bool', '--no-meep', '--multi=baz',
'--', '--not-a-flag', 'eek'
]),
{
c : true,
a : true,
t : true,
s : 'woo',
h : 'awesome',
b : true,
bool : true,
key : 'value',
multi : [ 'quux', 'baz' ],
meep : false,
name : 'meowmers',
_ : [ 'bare', '--not-a-flag', 'eek' ]
}
);
t.end();
});
test('flag boolean', function (t) {
var argv = parse([ '-t', 'moo' ], { boolean: 't' });
t.deepEqual(argv, { t : true, _ : [ 'moo' ] });
t.deepEqual(typeof argv.t, 'boolean');
t.end();
});
test('flag boolean value', function (t) {
var argv = parse(['--verbose', 'false', 'moo', '-t', 'true'], {
boolean: [ 't', 'verbose' ],
default: { verbose: true }
});
t.deepEqual(argv, {
verbose: false,
t: true,
_: ['moo']
});
t.deepEqual(typeof argv.verbose, 'boolean');
t.deepEqual(typeof argv.t, 'boolean');
t.end();
});
test('newlines in params' , function (t) {
var args = parse([ '-s', "X\nX" ])
t.deepEqual(args, { _ : [], s : "X\nX" });
// reproduce in bash:
// VALUE="new
// line"
// node program.js --s="$VALUE"
args = parse([ "--s=X\nX" ])
t.deepEqual(args, { _ : [], s : "X\nX" });
t.end();
});
test('strings' , function (t) {
var s = parse([ '-s', '0001234' ], { string: 's' }).s;
t.equal(s, '0001234');
t.equal(typeof s, 'string');
var x = parse([ '-x', '56' ], { string: 'x' }).x;
t.equal(x, '56');
t.equal(typeof x, 'string');
t.end();
});
test('stringArgs', function (t) {
var s = parse([ ' ', ' ' ], { string: '_' })._;
t.same(s.length, 2);
t.same(typeof s[0], 'string');
t.same(s[0], ' ');
t.same(typeof s[1], 'string');
t.same(s[1], ' ');
t.end();
});
test('empty strings', function(t) {
var s = parse([ '-s' ], { string: 's' }).s;
t.equal(s, '');
t.equal(typeof s, 'string');
var str = parse([ '--str' ], { string: 'str' }).str;
t.equal(str, '');
t.equal(typeof str, 'string');
var letters = parse([ '-art' ], {
string: [ 'a', 't' ]
});
t.equal(letters.a, '');
t.equal(letters.r, true);
t.equal(letters.t, '');
t.end();
});
test('string and alias', function(t) {
var x = parse([ '--str', '000123' ], {
string: 's',
alias: { s: 'str' }
});
t.equal(x.str, '000123');
t.equal(typeof x.str, 'string');
t.equal(x.s, '000123');
t.equal(typeof x.s, 'string');
var y = parse([ '-s', '000123' ], {
string: 'str',
alias: { str: 's' }
});
t.equal(y.str, '000123');
t.equal(typeof y.str, 'string');
t.equal(y.s, '000123');
t.equal(typeof y.s, 'string');
t.end();
});
test('slashBreak', function (t) {
t.same(
parse([ '-I/foo/bar/baz' ]),
{ I : '/foo/bar/baz', _ : [] }
);
t.same(
parse([ '-xyz/foo/bar/baz' ]),
{ x : true, y : true, z : '/foo/bar/baz', _ : [] }
);
t.end();
});
test('alias', function (t) {
var argv = parse([ '-f', '11', '--zoom', '55' ], {
alias: { z: 'zoom' }
});
t.equal(argv.zoom, 55);
t.equal(argv.z, argv.zoom);
t.equal(argv.f, 11);
t.end();
});
test('multiAlias', function (t) {
var argv = parse([ '-f', '11', '--zoom', '55' ], {
alias: { z: [ 'zm', 'zoom' ] }
});
t.equal(argv.zoom, 55);
t.equal(argv.z, argv.zoom);
t.equal(argv.z, argv.zm);
t.equal(argv.f, 11);
t.end();
});
test('nested dotted objects', function (t) {
var argv = parse([
'--foo.bar', '3', '--foo.baz', '4',
'--foo.quux.quibble', '5', '--foo.quux.o_O',
'--beep.boop'
]);
t.same(argv.foo, {
bar : 3,
baz : 4,
quux : {
quibble : 5,
o_O : true
}
});
t.same(argv.beep, { boop : true });
t.end();
});
+9
View File
@@ -0,0 +1,9 @@
var parse = require('../');
var test = require('tape');
test('parse with modifier functions' , function (t) {
t.plan(1);
var argv = parse([ '-b', '123' ], { boolean: 'b' });
t.deepEqual(argv, { b: true, _: [123] });
});
+44
View File
@@ -0,0 +1,44 @@
var parse = require('../');
var test = require('tape');
test('proto pollution', function (t) {
var argv = parse(['--__proto__.x','123']);
t.equal({}.x, undefined);
t.equal(argv.__proto__.x, undefined);
t.equal(argv.x, undefined);
t.end();
});
test('proto pollution (array)', function (t) {
var argv = parse(['--x','4','--x','5','--x.__proto__.z','789']);
t.equal({}.z, undefined);
t.deepEqual(argv.x, [4,5]);
t.equal(argv.x.z, undefined);
t.equal(argv.x.__proto__.z, undefined);
t.end();
});
test('proto pollution (number)', function (t) {
var argv = parse(['--x','5','--x.__proto__.z','100']);
t.equal({}.z, undefined);
t.equal((4).z, undefined);
t.equal(argv.x, 5);
t.equal(argv.x.z, undefined);
t.end();
});
test('proto pollution (string)', function (t) {
var argv = parse(['--x','abc','--x.__proto__.z','def']);
t.equal({}.z, undefined);
t.equal('...'.z, undefined);
t.equal(argv.x, 'abc');
t.equal(argv.x.z, undefined);
t.end();
});
test('proto pollution (constructor)', function (t) {
var argv = parse(['--constructor.prototype.y','123']);
t.equal({}.y, undefined);
t.equal(argv.y, undefined);
t.end();
});
+67
View File
@@ -0,0 +1,67 @@
var parse = require('../');
var test = require('tape');
test('numeric short args', function (t) {
t.plan(2);
t.deepEqual(parse([ '-n123' ]), { n: 123, _: [] });
t.deepEqual(
parse([ '-123', '456' ]),
{ 1: true, 2: true, 3: 456, _: [] }
);
});
test('short', function (t) {
t.deepEqual(
parse([ '-b' ]),
{ b : true, _ : [] },
'short boolean'
);
t.deepEqual(
parse([ 'foo', 'bar', 'baz' ]),
{ _ : [ 'foo', 'bar', 'baz' ] },
'bare'
);
t.deepEqual(
parse([ '-cats' ]),
{ c : true, a : true, t : true, s : true, _ : [] },
'group'
);
t.deepEqual(
parse([ '-cats', 'meow' ]),
{ c : true, a : true, t : true, s : 'meow', _ : [] },
'short group next'
);
t.deepEqual(
parse([ '-h', 'localhost' ]),
{ h : 'localhost', _ : [] },
'short capture'
);
t.deepEqual(
parse([ '-h', 'localhost', '-p', '555' ]),
{ h : 'localhost', p : 555, _ : [] },
'short captures'
);
t.end();
});
test('mixed short bool and capture', function (t) {
t.same(
parse([ '-h', 'localhost', '-fp', '555', 'script.js' ]),
{
f : true, p : 555, h : 'localhost',
_ : [ 'script.js' ]
}
);
t.end();
});
test('short and long', function (t) {
t.deepEqual(
parse([ '-h', 'localhost', '-fp', '555', 'script.js' ]),
{
f : true, p : 555, h : 'localhost',
_ : [ 'script.js' ]
}
);
t.end();
});
+15
View File
@@ -0,0 +1,15 @@
var parse = require('../');
var test = require('tape');
test('stops parsing on the first non-option when stopEarly is set', function (t) {
var argv = parse(['--aaa', 'bbb', 'ccc', '--ddd'], {
stopEarly: true
});
t.deepEqual(argv, {
aaa: 'bbb',
_: ['ccc', '--ddd']
});
t.end();
});
+102
View File
@@ -0,0 +1,102 @@
var parse = require('../');
var test = require('tape');
test('boolean and alias is not unknown', function (t) {
var unknown = [];
function unknownFn(arg) {
unknown.push(arg);
return false;
}
var aliased = [ '-h', 'true', '--derp', 'true' ];
var regular = [ '--herp', 'true', '-d', 'true' ];
var opts = {
alias: { h: 'herp' },
boolean: 'h',
unknown: unknownFn
};
var aliasedArgv = parse(aliased, opts);
var propertyArgv = parse(regular, opts);
t.same(unknown, ['--derp', '-d']);
t.end();
});
test('flag boolean true any double hyphen argument is not unknown', function (t) {
var unknown = [];
function unknownFn(arg) {
unknown.push(arg);
return false;
}
var argv = parse(['--honk', '--tacos=good', 'cow', '-p', '55'], {
boolean: true,
unknown: unknownFn
});
t.same(unknown, ['--tacos=good', 'cow', '-p']);
t.same(argv, {
honk: true,
_: []
});
t.end();
});
test('string and alias is not unknown', function (t) {
var unknown = [];
function unknownFn(arg) {
unknown.push(arg);
return false;
}
var aliased = [ '-h', 'hello', '--derp', 'goodbye' ];
var regular = [ '--herp', 'hello', '-d', 'moon' ];
var opts = {
alias: { h: 'herp' },
string: 'h',
unknown: unknownFn
};
var aliasedArgv = parse(aliased, opts);
var propertyArgv = parse(regular, opts);
t.same(unknown, ['--derp', '-d']);
t.end();
});
test('default and alias is not unknown', function (t) {
var unknown = [];
function unknownFn(arg) {
unknown.push(arg);
return false;
}
var aliased = [ '-h', 'hello' ];
var regular = [ '--herp', 'hello' ];
var opts = {
default: { 'h': 'bar' },
alias: { 'h': 'herp' },
unknown: unknownFn
};
var aliasedArgv = parse(aliased, opts);
var propertyArgv = parse(regular, opts);
t.same(unknown, []);
t.end();
unknownFn(); // exercise fn for 100% coverage
});
test('value following -- is not unknown', function (t) {
var unknown = [];
function unknownFn(arg) {
unknown.push(arg);
return false;
}
var aliased = [ '--bad', '--', 'good', 'arg' ];
var opts = {
'--': true,
unknown: unknownFn
};
var argv = parse(aliased, opts);
t.same(unknown, ['--bad']);
t.same(argv, {
'--': ['good', 'arg'],
'_': []
})
t.end();
});
+8
View File
@@ -0,0 +1,8 @@
var parse = require('../');
var test = require('tape');
test('whitespace should be whitespace' , function (t) {
t.plan(1);
var x = parse([ '-x', '\t' ]).x;
t.equal(x, '\t');
});
+2746 -2019
View File
File diff suppressed because it is too large Load Diff
+4 -3
View File
@@ -23,7 +23,8 @@
"dependencies": {
"@actions/core": "^1.0.0",
"@actions/exec": "^1.0.1",
"@actions/tool-cache": "^1.1.2"
"@actions/tool-cache": "^1.1.2",
"minimist": "^1.2.5"
},
"devDependencies": {
"@types/jest": "^24.0.13",
@@ -34,8 +35,8 @@
"eslint-config-prettier": "^6.5.0",
"eslint-plugin-import": "^2.18.2",
"eslint-plugin-prettier": "^3.1.1",
"jest": "^24.8.0",
"jest-circus": "^24.7.1",
"jest": "^25.2.3",
"jest-circus": "^25.2.3",
"prettier": "^1.18.2",
"ts-jest": "^24.0.2",
"typescript": "^3.5.1"
+16 -12
View File
@@ -1,8 +1,6 @@
import * as exec from '@actions/exec';
const EMULATOR_BOOT_TIMEOUT_SECONDS = 300;
const AVD_MANAGER_PATH = `${process.env.ANDROID_HOME}/tools/bin/avdmanager`;
const ADB_PATH = `${process.env.ANDROID_HOME}/platform-tools/adb`;
const EMULATOR_BOOT_TIMEOUT_SECONDS = 600;
/**
* Creates and launches a new AVD instance with the specified configurations.
@@ -11,14 +9,20 @@ export async function launchEmulator(apiLevel: number, target: string, arch: str
// create a new AVD
if (profile.trim() !== '') {
console.log(`Creating AVD with custom profile ${profile}`);
await exec.exec(`${AVD_MANAGER_PATH} create avd --force -n test --abi "${target}/${arch}" --package "system-images;android-${apiLevel};${target};${arch}" --device "${profile}"`);
await exec.exec(`avdmanager create avd --force -n test --abi "${target}/${arch}" --package "system-images;android-${apiLevel};${target};${arch}" --device "${profile}"`);
} else {
console.log(`Creating AVD without custom profile.`);
await exec.exec(`sh -c \\"echo no | ${AVD_MANAGER_PATH} create avd --force -n test --abi '${target}/${arch}' --package 'system-images;android-${apiLevel};${target};${arch}'"`);
await exec.exec(`sh -c \\"echo no | avdmanager create avd --force -n test --abi '${target}/${arch}' --package 'system-images;android-${apiLevel};${target};${arch}'"`);
}
// start emulator
console.log('Starting emulator.');
// turn off hardware acceleration on Linux
if (process.platform === 'linux') {
emulatorOptions += ' -accel off';
}
await exec.exec(`sh -c \\"${process.env.ANDROID_HOME}/emulator/emulator -avd test ${emulatorOptions} &"`, [], {
listeners: {
stderr: (data: Buffer) => {
@@ -31,14 +35,14 @@ export async function launchEmulator(apiLevel: number, target: string, arch: str
// wait for emulator to complete booting
await waitForDevice();
await exec.exec(`${ADB_PATH} shell input keyevent 82`);
await exec.exec(`adb shell input keyevent 82`);
// disable animations
if (disableAnimations) {
console.log('Disabling animations.');
await exec.exec(`${ADB_PATH} shell settings put global window_animation_scale 0.0`);
await exec.exec(`${ADB_PATH} shell settings put global transition_animation_scale 0.0`);
await exec.exec(`${ADB_PATH} shell settings put global animator_duration_scale 0.0`);
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`);
}
}
@@ -47,7 +51,7 @@ export async function launchEmulator(apiLevel: number, target: string, arch: str
*/
export async function killEmulator(): Promise<void> {
try {
await exec.exec(`${ADB_PATH} -s emulator-5554 emu kill`);
await exec.exec(`adb -s emulator-5554 emu kill`);
} catch (error) {
console.log(error.message);
}
@@ -64,7 +68,7 @@ async function waitForDevice(): Promise<void> {
while (!booted) {
try {
let result = '';
await exec.exec(`${ADB_PATH} shell getprop sys.boot_completed`, [], {
await exec.exec(`adb shell getprop sys.boot_completed`, [], {
listeners: {
stdout: (data: Buffer) => {
result += data.toString();
@@ -77,7 +81,7 @@ async function waitForDevice(): Promise<void> {
break;
}
} catch (error) {
console.log(error.message);
console.warn(error.message);
}
if (attempts < maxAttemps) {
+1 -1
View File
@@ -1,4 +1,4 @@
export const MIN_API_LEVEL = 21;
export const MIN_API_LEVEL = 15;
export const VALID_TARGETS: Array<string> = ['default', 'google_apis'];
export const VALID_ARCHS: Array<string> = ['x86', 'x86_64'];
-39
View File
@@ -1,39 +0,0 @@
import * as core from '@actions/core';
import * as exec from '@actions/exec';
/**
* Returns the current $JAVA_HOME path.
*/
export async function getCurrentJavaHome(): Promise<string> {
let defaultJavaHome = '';
await exec.exec(`sh -c \\"echo $JAVA_HOME"`, [], {
listeners: {
stdout: (data: Buffer) => {
defaultJavaHome += data.toString();
}
}
});
return defaultJavaHome.trim();
}
/**
* Returns the Java 8 $JAVA_HOME path.
*/
export async function getJavaHomeV8(): Promise<string> {
let javaHomeV8 = '';
await exec.exec(`/usr/libexec/java_home -v 1.8`, [], {
listeners: {
stdout: (data: Buffer) => {
javaHomeV8 += data.toString();
}
}
});
return javaHomeV8.trim();
}
/**
* Sets $JAVA_HOME to the specified path.
*/
export function setJavaHome(path: string) {
core.exportVariable('JAVA_HOME', path);
}
+37 -18
View File
@@ -4,13 +4,18 @@ import { checkApiLevel, checkTarget, checkArch, checkDisableAnimations, checkEmu
import { launchEmulator, killEmulator } from './emulator-manager';
import * as exec from '@actions/exec';
import { parseScript } from './script-parser';
import { getCurrentJavaHome, getJavaHomeV8, setJavaHome } from './java-version-manager';
async function run() {
try {
// only support running on macOS
// only support running on macOS or Linux
if (process.platform !== 'darwin') {
throw new Error('This action is expected to be run within a macOS virtual machine to enable hardware acceleration.');
if (process.platform === 'linux') {
console.warn(
`You're running a Linux VM where hardware acceleration is not available. Please consider using a macOS VM instead to take advantage of native hardware acceleration support provided by HAXM.`
);
} else {
throw new Error('Unsupported virtual machine: please use either macos or ubuntu VM.');
}
}
// API level of the platform and system image
@@ -51,6 +56,27 @@ async function run() {
}
const emulatorBuild = !emulatorBuildInput ? undefined : emulatorBuildInput;
// custom working directory
const workingDirectoryInput = core.getInput('working-directory');
if (workingDirectoryInput) {
console.log(`custom working directory: ${workingDirectoryInput}`);
}
const workingDirectory = !workingDirectoryInput ? undefined : workingDirectoryInput;
// version of NDK to install
const ndkInput = core.getInput('ndk');
if (ndkInput) {
console.log(`version of NDK to install: ${ndkInput}`);
}
const ndkVersion = !ndkInput ? undefined : ndkInput;
// version of CMake to install
const cmakeInput = core.getInput('cmake');
if (cmakeInput) {
console.log(`version of CMake to install: ${cmakeInput}`);
}
const cmakeVersion = !cmakeInput ? undefined : cmakeInput;
// custom script to run
const scriptInput = core.getInput('script', { required: true });
const scripts = parseScript(scriptInput);
@@ -59,27 +85,20 @@ async function run() {
console.log(`${script}`);
});
// use Java 8 for sdkmanager and avdmanager
const defaultJavaHome = await getCurrentJavaHome();
setJavaHome(await getJavaHomeV8());
// install SDK
await installAndroidSdk(apiLevel, target, arch, emulatorBuild);
await installAndroidSdk(apiLevel, target, arch, emulatorBuild, ndkVersion, cmakeVersion);
try {
// launch an emulator
await launchEmulator(apiLevel, target, arch, profile, emulatorOptions, disableAnimations);
} catch (error) {
core.setFailed(error.message);
}
// use default JAVA_HOME for running custom script
setJavaHome(defaultJavaHome);
// launch an emulator
await launchEmulator(apiLevel, target, arch, profile, emulatorOptions, disableAnimations);
// execute the custom script
try {
// move to custom working directory if set
if (workingDirectory) {
process.chdir(workingDirectory);
}
for (const script of scripts) {
await exec.exec(`${script}`);
await exec.exec(`sh -c \\"${script}"`);
}
} catch (error) {
core.setFailed(error.message);
+38 -9
View File
@@ -1,25 +1,54 @@
import * as core from '@actions/core';
import * as exec from '@actions/exec';
const BUILD_TOOLS_VERSION = '29.0.3';
const CMDLINE_TOOLS_URL_MAC = 'https://dl.google.com/android/repository/commandlinetools-mac-6200805_latest.zip';
const CMDLINE_TOOLS_URL_LINUX = 'https://dl.google.com/android/repository/commandlinetools-linux-6200805_latest.zip';
/**
* Installs & updates the Android SDK for the macOS platform, including SDK platform for the chosen API level, latest build tools, platform tools, Android Emulator,
* and the system image for the chosen API level, CPU arch, and target.
*/
export async function installAndroidSdk(apiLevel: number, target: string, arch: string, emulatorBuild?: string): Promise<void> {
const sdkmangerPath = `${process.env.ANDROID_HOME}/tools/bin/sdkmanager`;
export async function installAndroidSdk(apiLevel: number, target: string, arch: string, emulatorBuild?: string, ndkVersion?: string, cmakeVersion?: string): Promise<void> {
const isOnMac = process.platform === 'darwin';
console.log('Installing new cmdline-tools.');
const sdkUrl = isOnMac ? CMDLINE_TOOLS_URL_MAC : CMDLINE_TOOLS_URL_LINUX;
await exec.exec(`sudo mkdir ${process.env.ANDROID_HOME}/cmdline-tools`);
await exec.exec(`curl -fo commandlinetools.zip ${sdkUrl}`);
await exec.exec(`sudo unzip -q commandlinetools.zip -d ${process.env.ANDROID_HOME}/cmdline-tools`);
await exec.exec(`sudo rm -f commandlinetools.zip`);
// add paths for commandline-tools and platform-tools
core.addPath(`${process.env.ANDROID_HOME}/cmdline-tools/tools:${process.env.ANDROID_HOME}/cmdline-tools/tools/bin:${process.env.ANDROID_HOME}/platform-tools`);
// additional permission and license requirements for Linux
if (!isOnMac) {
await exec.exec(`sh -c \\"sudo chmod -R 777 ${process.env.ANDROID_HOME}"`);
await exec.exec(`sh -c \\"echo -e '\n84831b9409646a918e30573bab4c9c91346d8abd' > ${process.env.ANDROID_HOME}/licenses/android-sdk-preview-license"`);
}
console.log('Installing latest build tools, platform tools, and platform.');
await exec.exec(`sh -c \\"${sdkmangerPath} --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"`);
if (emulatorBuild) {
console.log(`Installing emulator build ${emulatorBuild}.`);
await exec.exec(`curl -fo emulator.zip https://dl.google.com/android/repository/emulator-darwin-${emulatorBuild}.zip`);
await exec.exec(`rm -rf ${process.env.ANDROID_HOME}/emulator`);
await exec.exec(`unzip -q emulator.zip -d ${process.env.ANDROID_HOME}`);
await exec.exec(`rm -f emulator.zip`);
await exec.exec(`curl -fo emulator.zip https://dl.google.com/android/repository/emulator-${isOnMac ? 'darwin' : 'linux'}-${emulatorBuild}.zip`);
await exec.exec(`sudo rm -rf ${process.env.ANDROID_HOME}/emulator`);
await exec.exec(`sudo unzip -q emulator.zip -d ${process.env.ANDROID_HOME}`);
await exec.exec(`sudo rm -f emulator.zip`);
} else {
console.log('Installing latest emulator.');
await exec.exec(`sh -c \\"${sdkmangerPath} --install emulator > /dev/null"`);
await exec.exec(`sh -c \\"sdkmanager --install emulator > /dev/null"`);
}
console.log('Installing system images.');
await exec.exec(`sh -c \\"${sdkmangerPath} --install 'system-images;android-${apiLevel};${target};${arch}' > /dev/null"`);
await exec.exec(`sh -c \\"sdkmanager --install 'system-images;android-${apiLevel};${target};${arch}' > /dev/null"`);
if (ndkVersion) {
console.log(`Installing NDK ${ndkVersion}.`);
await exec.exec(`sh -c \\"sdkmanager --install 'ndk;${ndkVersion}' > /dev/null"`);
}
if (cmakeVersion) {
console.log(`Installing CMake ${cmakeVersion}.`);
await exec.exec(`sh -c \\"sdkmanager --install 'cmake;${cmakeVersion}' > /dev/null"`);
}
}
+3 -3
View File
@@ -8,7 +8,7 @@ android {
defaultConfig {
applicationId "com.example.testapp"
minSdkVersion 21
minSdkVersion 15
targetSdkVersion 29
versionCode 1
versionName "1.0"
@@ -28,9 +28,9 @@ dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.1.0'
implementation 'androidx.core:core-ktx:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
testImplementation 'junit:junit:4.13'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.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.
buildscript {
ext.kotlin_version = '1.3.61'
ext.kotlin_version = '1.3.70'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.3'
classpath 'com.android.tools.build:gradle:3.6.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// 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
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip