Compare commits

...

47 Commits

Author SHA1 Message Date
Yang 2196379783 Test API 23-30. 2020-10-30 20:52:34 +11:00
Yang 448508a511 don't fail fast. 2020-10-30 20:41:01 +11:00
Yang fd437876c6 Try API 23-29. 2020-10-30 20:34:44 +11:00
Yang 517fe314d7 WIP - Test sdcard. 2020-10-30 20:31:43 +11:00
Daniel Jette 4690ba2920 Add Shopify/android-testify to list of projects
Shopify/android-testify uses the Android Emulator Runner for GitHub Actions
2020-10-27 08:42:51 +11:00
Yang 15da0b2f14 Prepare for release 2.12.0. 2020-10-16 14:39:02 +11:00
Yang 53151f5236 Add support for using google_apis_playstore system images. 2020-10-16 14:32:01 +11:00
Adrián Nieto Rodríguez fd240e46a6 Fix README.md typo 2020-10-15 00:01:10 +11:00
Yang 04a32d7ac6 Prepare for release 2.11.1. 2020-10-08 12:20:45 +11:00
Yang 1eebfdbd84 Update npm packages. 2020-10-08 12:14:51 +11:00
dependabot[bot] 47cc04b42b Bump @actions/core from 1.2.4 to 1.2.6
Bumps [@actions/core](https://github.com/actions/toolkit/tree/HEAD/packages/core) from 1.2.4 to 1.2.6.
- [Release notes](https://github.com/actions/toolkit/releases)
- [Changelog](https://github.com/actions/toolkit/blob/main/packages/core/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/core)

Signed-off-by: dependabot[bot] <support@github.com>
2020-10-02 03:38:16 +10:00
Yang dd0984ca1b Update to gradle 6.7-rc-1 for test fixture. Build with Java 15 on CI. 2020-09-20 18:50:55 +10:00
Yang fef056dbdf Improve GitHub Actions Gradle cache. 2020-09-03 14:55:52 +10:00
Yang 31e4885c24 Add square/radiography to adopter list 2020-09-02 13:05:03 +10:00
Yang a59d5e6807 Add cashapp/copper to adapter list 2020-08-16 19:13:51 +10:00
Yang 8feb8a564a SDK Build tools 30.0.2. 2020-08-15 15:00:23 +10:00
Yang d677dca18c Update fixture dependencies. 2020-08-15 00:01:17 +10:00
Yang d23e781d9a Gradle 6.6. 2020-08-11 10:34:30 +10:00
Vinay Shenoy 29a8a60dbb Update README.md
We use this action for running instrumented tests in CI. Thanks for the great work!
2020-07-28 22:24:22 +10:00
Yang 327f6f1c1f Update packages. 2020-07-18 19:43:39 +10:00
Yang 1dc0441870 Build tools 30.0.1. 2020-07-09 19:49:16 +10:00
Yang fb3c06d709 Use fs for writing licence files. 2020-07-03 16:14:01 +10:00
Yang e22ecdedee Update cmdline-tools to 2.1. 2020-06-24 23:39:11 +10:00
Yang a72b5eb6c0 Prepare for release 2.11.0. 2020-06-20 19:52:48 +10:00
Yang b39f1a9f8b Support running multiple actions sequentially in a single job. 2020-06-20 19:48:18 +10:00
Yang a4d07e10d7 Add RxBinding to adopter list. 2020-06-19 14:46:44 +10:00
Yang 6f6ca5ebc6 Rename master branch to main. 2020-06-18 19:26:07 +10:00
Yang 9ef7536c4b Prepare for release 2.10.0. 2020-06-12 19:50:37 +10:00
Yang 8d0ce4c94b Support API 30 system images and add API 30 to workflow. Update build tools to 30.0.0. Update test projec’s compileSdk and targetSdk to API 30. 2020-06-12 19:44:21 +10:00
Yang fc17953a7f Prepare for release 2.9.0. 2020-05-29 23:10:13 +10:00
Yang d122d1159d Update test fixture dependencies. 2020-05-29 21:48:25 +10:00
Yang 736093576f SDK Command-line Tools 2.0. 2020-05-29 21:48:25 +10:00
Yang a06469d17c Add information on running on Linux to README.md. 2020-05-21 18:11:25 +10:00
Yang 3609b3300b Fix doc. 2020-05-10 01:07:23 +10:00
vgaidarji 9b15ac23ac Mention ci-matters repo in adopters list 2020-05-09 21:41:51 +10:00
Yang 4df9257dba Prepare for release 2.8.0. 2020-05-01 20:18:07 +10:00
Yang f5435af41b Add support for specifying a custom name used for creating AVD. 2020-05-01 20:11:25 +10:00
Yang fdd35a3667 Fix typo 2020-04-25 18:52:46 +10:00
Yang 2513ea5ecf Add list of projects using android-emulator-runner. 2020-04-25 18:17:11 +10:00
Yang c0431f2812 Bump kotlin and AGP for test fixture project. 2020-04-14 23:57:49 +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 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
21 changed files with 3725 additions and 1568 deletions
+56 -18
View File
@@ -3,25 +3,43 @@ on:
pull_request: pull_request:
push: push:
branches: branches:
- master # - main
- 'yc/*'
paths-ignore: paths-ignore:
- '**.md' - '**.md'
jobs: jobs:
test: test:
runs-on: ${{ matrix.os }} # runs-on: ${{ matrix.os }}
runs-on: macos-latest
env:
JAVA_TOOL_OPTIONS: -Xmx4g
timeout-minutes: 15 timeout-minutes: 15
strategy: strategy:
fail-fast: false
matrix: matrix:
os: [macos-latest, ubuntu-latest] # os: [macos-latest, ubuntu-latest]
api-level: [16, 23, 28, 29] api-level: [23, 24, 25, 26, 27, 28, 29, 30]
exclude: # target: [default, google_apis]
- os: ubuntu-latest # exclude:
api-level: 23 # - os: ubuntu-latest
- os: ubuntu-latest # api-level: 23
api-level: 29 # - os: ubuntu-latest
- os: macos-latest # api-level: 29
api-level: 28 # - target: google_apis
# api-level: 16
# - target: google_apis
# api-level: 23
# - target: google_apis
# api-level: 29
# include:
# - os: macos-latest
# api-level: 30
# target: google_apis
# - os: macos-latest
# api-level: 24
# target: playstore
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
@@ -31,28 +49,48 @@ jobs:
- name: build, test and lint - name: build, test and lint
run: | run: |
npm install npm ci
npm run build npm run build
npm run lint npm run lint
npm test npm test
- name: Java 14 - name: Java 15
uses: actions/setup-java@v1 uses: actions/setup-java@v1
with: with:
java-version: 14 java-version: 15
- uses: actions/cache@v2
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }}
- name: run action - name: run action
uses: ./ uses: ./
with: with:
api-level: ${{ matrix.api-level }} api-level: ${{ matrix.api-level }}
target: default target: default
arch: x86 arch: x86
profile: Nexus 6 # profile: Nexus 6
profile: pixel_3a
# avd-name: test
emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim -camera-back none emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim -camera-back none
disable-animations: true disable-animations: true
working-directory: ./test-fixture/ working-directory: ./test-fixture/
script: | script: |
echo $GITHUB_REPOSITORY echo $GITHUB_REPOSITORY
adb devices adb devices
./gradlew help adb shell ls -la /sdcard
./gradlew connectedDebugAndroidTest adb shell ls -la /storage
adb emu geo fix 0 51
adb shell screenrecord --verbose /sdcard/capture.mpg &
adb shell pkill -INT screenrecord
mkdir -p app/build/report
cd app/build/report && adb pull /sdcard/capture.mpg
# ./gradlew help
# ./gradlew connectedDebugAndroidTest
- name: Upload build artifacts
uses: actions/upload-artifact@v1
with:
name: build-outputs
path: ./test-fixture/app/build/report
+44
View File
@@ -1,5 +1,49 @@
# Change Log # Change Log
## v2.12.0
Added support for using the `playstore` system images:
```
- name: run tests
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 30
target: playstore
arch: x86
script: ./gradlew connectedCheck
```
## v2.11.1
* Update SDK command-line tools to `2.1`.
* Update @actions/core to `1.2.6`.
## v2.11.0
* Support running multiple instances of the action sequentially in a single job - [#73](https://github.com/ReactiveCircus/android-emulator-runner/issues/73).
## v2.10.0
* Support Android 11 (API 30) system images.
* Bump build tools to `30.0.0`.
## v2.9.0
* Updated to SDK command-line tools `2.0`.
## v2.8.0
* Added support for specifying a custom name used for creating the **AVD** - [#59](https://github.com/ReactiveCircus/android-emulator-runner/issues/59).
## 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 ## 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). * Fixed SDK license issue on Linux when downloading API 28+ system images - [#42](https://github.com/ReactiveCircus/android-emulator-runner/issues/42).
+60 -9
View File
@@ -4,11 +4,11 @@
<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> <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> </p>
A GitHub Action for installing, configuring and running hardware-accelerated Android Emulators on macOS virtual machines (or Linux virtual machines but without hardware accleration). A GitHub Action for installing, configuring and running hardware-accelerated Android Emulators on macOS virtual machines.
The old ARM-based emulators were slow and are no longer supported by Google. The modern Intel Atom (x86 and x86_64) emulators require hardware acceleration (HAXM on Mac & Windows, QEMU on Linux) from the host to run fast. This presents a challenge on CI as to be able to run hardware accelerated emulators within a docker container, **KVM** must be supported by the host VM which isn't the case for cloud-based CI providers due to infrastructural limits. If you want to learn more about this, here's an article I wrote: [Running Android Instrumented Tests on CI](https://dev.to/ychescale9/running-android-emulators-on-ci-from-bitrise-io-to-github-actions-3j76). The old ARM-based emulators were slow and are no longer supported by Google. The modern Intel Atom (x86 and x86_64) emulators require hardware acceleration (HAXM on Mac & Windows, QEMU on Linux) from the host to run fast. This presents a challenge on CI as to be able to run hardware accelerated emulators within a docker container, **KVM** must be supported by the host VM which isn't the case for cloud-based CI providers due to infrastructural limits. If you want to learn more about this, here's an article I wrote: [Running Android Instrumented Tests on CI](https://dev.to/ychescale9/running-android-emulators-on-ci-from-bitrise-io-to-github-actions-3j76).
The **masOS** VM provided by **GitHub Actions** has **HAXM** installed so we are able to create a new AVD instance, launch an emulator with hardware acceleration, and run our Android The **macOS** VM provided by **GitHub Actions** has **HAXM** installed so we are able to create a new AVD instance, launch an emulator with hardware acceleration, and run our Android
tests directly on the VM. tests directly on the VM.
This action automates the process by doing the following: This action automates the process by doing the following:
@@ -24,8 +24,6 @@ This action automates the process by doing the following:
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**. 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**: A workflow that uses **android-emulator-runner** to run your instrumented tests on **API 29**:
``` ```
@@ -67,20 +65,73 @@ jobs:
script: ./gradlew connectedCheck 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 ## Configurations
| | **Required** | **Default** | **Description** | | **Input** | **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 15**. | | `api-level` | Required | N/A | API level of the platform system image - e.g. 23 for Android Marshmallow, 29 for Android 10. **Minimum API level supported is 15**. |
| `target` | Optional | `default` | Target of the system image - `default` or `google_apis`. | | `target` | Optional | `default` | Target of the system image - `default`, `google_apis` or `playstore`. |
| `arch` | Optional | `x86` | CPU architecture of the system image - `x86` or `x86_64`. Note that `x86_64` image is only available for API 21+. | | `arch` | Optional | `x86` | CPU architecture of the system image - `x86` 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". | | `profile` | Optional | N/A | Hardware profile used for creating the AVD - e.g. `Nexus 6`. For a list of all profiles available, run `avdmanager list` and refer to the results under "Available Android Virtual Devices". |
| `avd-name` | Optional | `test` | Custom AVD name used for creating the Android Virtual Device. |
| `emulator-options` | Optional | See below | Command-line options used when launching the emulator (replacing all default options) - e.g. `-no-window -no-snapshot -camera-back emulated`. | | `emulator-options` | Optional | See below | Command-line options used when launching the emulator (replacing all default options) - e.g. `-no-window -no-snapshot -camera-back emulated`. |
| `disable-animations` | Optional | `true` | Whether to disable animations - `true` or `false`. | | `disable-animations` | Optional | `true` | Whether to disable animations - `true` or `false`. |
| `emulator-build` | Optional | N/A | Build number of a specific version of the emulator binary to use e.g. `6061023` for emulator v29.3.0.0. | | `emulator-build` | Optional | N/A | Build number of a specific version of the emulator binary to use e.g. `6061023` for emulator v29.3.0.0. |
| `working-directory` | Optional | `./` | A custom working directory - e.g. `./android` if your root Gradle project is under the `./android` sub-directory within your repository. | | `working-directory` | Optional | `./` | A custom working directory - e.g. `./android` if your root Gradle project is under the `./android` sub-directory within your repository. |
| `ndk` | Optional | N/A | Version of NDK to install - e.g. `21.0.6113669` |
| `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` | | `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`. 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._ ## Can I use this action on Linux VMs?
The short answer is yes but it's expected to be a much worse experience (on some newer API levels it might not work at all) than running it on macOS.
For a longer answer please refer to [this issue](https://github.com/ReactiveCircus/android-emulator-runner/issues/46).
## Who is using Android Emulator Runner?
These are some of the open-source projects using (or used) **Android Emulator Runner**:
- [coil-kt/coil](https://github.com/coil-kt/coil/blob/master/.github/workflows/)
- [cashapp/sqldelight](https://github.com/cashapp/sqldelight/blob/master/.github/workflows/)
- [square/workflow](https://github.com/square/workflow/blob/main/.github/workflows/)
- [square/retrofit](https://github.com/square/retrofit/blob/master/.github/workflows/)
- [natario1/CameraView](https://github.com/natario1/CameraView/tree/master/.github/workflows)
- [natario1/Transcoder](https://github.com/natario1/Transcoder/tree/master/.github/workflows)
- [chrisbanes/insetter](https://github.com/chrisbanes/insetter/tree/main/.github/workflows)
- [slackhq/keeper](https://github.com/slackhq/keeper/tree/main/.github/workflows)
- [android/compose-samples](https://github.com/android/compose-samples/blob/master/.github/workflows/ci.yaml)
- [ReactiveCircus/streamlined](https://github.com/ReactiveCircus/streamlined/tree/main/.github/workflows)
- [ReactiveCircus/FlowBinding](https://github.com/ReactiveCircus/FlowBinding)
- [JakeWharton/RxBinding](https://github.com/JakeWharton/RxBinding/tree/master/.github/workflows)
- [vinaygaba/Learn-Jetpack-Compose-By-Example](https://github.com/vinaygaba/Learn-Jetpack-Compose-By-Example/)
- [ashishb/adb-enhanced](https://github.com/ashishb/adb-enhanced/tree/master/.github/workflows)
- [vgaidarji/ci-matters](https://github.com/vgaidarji/ci-matters/blob/master/.github/workflows/main.yaml)
- [simpledotorg/simple-android](https://github.com/simpledotorg/simple-android)
- [cashapp/copper](https://github.com/cashapp/copper/blob/trunk/.github/workflows/build.yaml)
- [square/radiography](https://github.com/square/radiography/blob/main/.github/workflows/android.yml)
- [Shopify/android-testify](https://github.com/Shopify/android-testify/blob/master/.github/workflows/sample_build.yml)
If you are using **Android Emulator Runner** and want your project included in the list, please feel free to create an issue or open a pull request.
+3 -3
View File
@@ -4,7 +4,7 @@ Refer to the [recommendeations for versioning and releasing actions](https://git
## New major release ## New major release
- From `master` branch, run `npm run build && npm test --clean && npm run lint` to make sure `lib/*.js` are up-to-date. - From `main` branch, run `npm run build && npm test --clean && npm run lint` to make sure `lib/*.js` are up-to-date.
- Update `CHANGELOG.md`, push change with "Prepare for release X.Y.Z." (where X.Y.Z is the new version). - Update `CHANGELOG.md`, push change with "Prepare for release X.Y.Z." (where X.Y.Z is the new version).
- Create a new branch e.g. `release/v1`, comment out `node_modules/` in `.gitignore`, commit the change (do not commit yet `node_modules`). - Create a new branch e.g. `release/v1`, comment out `node_modules/` in `.gitignore`, commit the change (do not commit yet `node_modules`).
- Run `npm prune --production`. - Run `npm prune --production`.
@@ -17,9 +17,9 @@ Refer to the [recommendeations for versioning and releasing actions](https://git
## New minor / patch release ## New minor / patch release
- From `master` branch, run `npm run build && npm test --clean && npm run lint` to make sure `lib/*.js` are up-to-date. - From `main` branch, run `npm run build && npm test --clean && npm run lint` to make sure `lib/*.js` are up-to-date.
- Update `CHANGELOG.md`, push change with "Prepare for release X.Y.Z." (where X.Y.Z is the new version). - Update `CHANGELOG.md`, push change with "Prepare for release X.Y.Z." (where X.Y.Z is the new version).
- Merge from `master` into the release branch e.g. `release/v1`. - Merge from `main` into the release branch e.g. `release/v1`.
- Run `npm prune --production`. - Run `npm prune --production`.
- Commit merged changes (and the pruned `node_modules`). - Commit merged changes (and the pruned `node_modules`).
- Push to remote. - Push to remote.
+5
View File
@@ -53,6 +53,11 @@ describe('target validator tests', () => {
validator.checkTarget('google_apis'); validator.checkTarget('google_apis');
}; };
expect(func2).not.toThrow(); expect(func2).not.toThrow();
const func3 = () => {
validator.checkTarget('google_apis');
};
expect(func3).not.toThrow();
}); });
}); });
+13 -4
View File
@@ -9,21 +9,30 @@ inputs:
description: 'API level of the platform and system image - e.g. 23 for Android Marshmallow, 29 for Android 10' description: 'API level of the platform and system image - e.g. 23 for Android Marshmallow, 29 for Android 10'
required: true required: true
target: target:
description: 'target of the system image - default or google_apis' description: 'target of the system image - default, google_apis or playstore'
default: 'default' default: 'default'
arch: arch:
description: 'CPU architecture of the system image - x86 or x86_64' description: 'CPU architecture of the system image - x86 or x86_64'
default: 'x86' default: 'x86'
profile: profile:
description: 'Hardware profile used for creating the AVD - e.g. `Nexus 6`.' description: 'hardware profile used for creating the AVD - e.g. `Nexus 6`'
avd-name:
description: 'custom AVD name used for creating the Android Virtual Device'
default: 'test'
emulator-options: emulator-options:
description: 'command-line options used when launching the emulator - e.g. `-no-window -no-snapshot -camera-back emulated`.' description: 'command-line options used when launching the emulator - e.g. `-no-window -no-snapshot -camera-back emulated`'
default: '-no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim' default: '-no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim'
disable-animations: disable-animations:
description: 'whether to disable animations - true or false' description: 'whether to disable animations - true or false'
default: 'true' default: 'true'
emulator-build: emulator-build:
description: 'build number of a specific version of the emulator binary to use e.g. `6061023` for emulator v29.3.0.0.' description: 'build number of a specific version of the emulator binary to use - e.g. `6061023` for emulator v29.3.0.0'
working-directory:
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: script:
description: 'custom script to run - e.g. `./gradlew connectedCheck`' description: 'custom script to run - e.g. `./gradlew connectedCheck`'
required: true required: true
+28 -14
View File
@@ -1,35 +1,49 @@
"use strict"; "use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
return new (P || (P = Promise))(function (resolve, reject) { if (k2 === undefined) k2 = k;
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } }) : (function(o, m, k, k2) {
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } if (k2 === undefined) k2 = k;
step((generator = generator.apply(thisArg, _arguments || [])).next()); o[k2] = m[k];
}); }));
}; var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) { var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod; if (mod && mod.__esModule) return mod;
var result = {}; var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
result["default"] = mod; __setModuleDefault(result, mod);
return result; return result;
}; };
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
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) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.killEmulator = exports.launchEmulator = void 0;
const exec = __importStar(require("@actions/exec")); const exec = __importStar(require("@actions/exec"));
const EMULATOR_BOOT_TIMEOUT_SECONDS = 600; 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, emulatorOptions, disableAnimations) { function launchEmulator(apiLevel, target, arch, profile, avdName, emulatorOptions, disableAnimations) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
// create a new AVD // create a new AVD
if (profile.trim() !== '') { if (profile.trim() !== '') {
console.log(`Creating AVD with custom profile ${profile}`); console.log(`Creating AVD with custom profile ${profile}`);
yield exec.exec(`avdmanager 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 "${avdName}" --abi "${target}/${arch}" --package "system-images;android-${apiLevel};${target};${arch}" --device "${profile}"`);
} }
else { else {
console.log(`Creating AVD without custom profile.`); console.log(`Creating AVD without custom profile.`);
yield exec.exec(`sh -c \\"echo no | avdmanager 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 "${avdName}" --abi '${target}/${arch}' --package 'system-images;android-${apiLevel};${target};${arch}'"`);
} }
// start emulator // start emulator
console.log('Starting emulator.'); console.log('Starting emulator.');
@@ -37,7 +51,7 @@ function launchEmulator(apiLevel, target, arch, profile, emulatorOptions, disabl
if (process.platform === 'linux') { if (process.platform === 'linux') {
emulatorOptions += ' -accel off'; emulatorOptions += ' -accel off';
} }
yield exec.exec(`sh -c \\"${process.env.ANDROID_HOME}/emulator/emulator -avd test ${emulatorOptions} &"`, [], { yield exec.exec(`sh -c \\"${process.env.ANDROID_HOME}/emulator/emulator -avd "${avdName}" ${emulatorOptions} &"`, [], {
listeners: { listeners: {
stderr: (data) => { stderr: (data) => {
if (data.toString().includes('invalid command-line parameter')) { if (data.toString().includes('invalid command-line parameter')) {
+2 -1
View File
@@ -1,7 +1,8 @@
"use strict"; "use strict";
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.checkEmulatorBuild = exports.checkDisableAnimations = exports.checkArch = exports.checkTarget = exports.checkApiLevel = exports.VALID_ARCHS = exports.VALID_TARGETS = exports.MIN_API_LEVEL = void 0;
exports.MIN_API_LEVEL = 15; exports.MIN_API_LEVEL = 15;
exports.VALID_TARGETS = ['default', 'google_apis']; exports.VALID_TARGETS = ['default', 'google_apis', 'google_apis_playstore'];
exports.VALID_ARCHS = ['x86', 'x86_64']; exports.VALID_ARCHS = ['x86', 'x86_64'];
function checkApiLevel(apiLevel) { function checkApiLevel(apiLevel) {
if (isNaN(Number(apiLevel)) || !Number.isInteger(Number(apiLevel))) { if (isNaN(Number(apiLevel)) || !Number.isInteger(Number(apiLevel))) {
+42 -13
View File
@@ -1,19 +1,32 @@
"use strict"; "use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
return new (P || (P = Promise))(function (resolve, reject) { if (k2 === undefined) k2 = k;
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } }) : (function(o, m, k, k2) {
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } if (k2 === undefined) k2 = k;
step((generator = generator.apply(thisArg, _arguments || [])).next()); o[k2] = m[k];
}); }));
}; var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) { var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod; if (mod && mod.__esModule) return mod;
var result = {}; var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
result["default"] = mod; __setModuleDefault(result, mod);
return result; return result;
}; };
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
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) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
const core = __importStar(require("@actions/core")); const core = __importStar(require("@actions/core"));
const sdk_installer_1 = require("./sdk-installer"); const sdk_installer_1 = require("./sdk-installer");
@@ -39,7 +52,8 @@ function run() {
const apiLevel = Number(apiLevelInput); const apiLevel = Number(apiLevelInput);
console.log(`API level: ${apiLevel}`); console.log(`API level: ${apiLevel}`);
// target of the system image // target of the system image
const target = core.getInput('target'); const targetInput = core.getInput('target');
const target = targetInput == 'playstore' ? 'google_apis_playstore' : targetInput;
input_validator_1.checkTarget(target); input_validator_1.checkTarget(target);
console.log(`target: ${target}`); console.log(`target: ${target}`);
// CPU architecture of the system image // CPU architecture of the system image
@@ -49,6 +63,9 @@ function run() {
// Hardware profile used for creating the AVD // Hardware profile used for creating the AVD
const profile = core.getInput('profile'); const profile = core.getInput('profile');
console.log(`Hardware profile: ${profile}`); console.log(`Hardware profile: ${profile}`);
// custom name used for creating the AVD
const avdName = core.getInput('avd-name');
console.log(`AVD name: ${avdName}`);
// emulator options // emulator options
const emulatorOptions = core.getInput('emulator-options').trim(); const emulatorOptions = core.getInput('emulator-options').trim();
console.log(`emulator options: ${emulatorOptions}`); console.log(`emulator options: ${emulatorOptions}`);
@@ -70,6 +87,18 @@ function run() {
console.log(`custom working directory: ${workingDirectoryInput}`); console.log(`custom working directory: ${workingDirectoryInput}`);
} }
const workingDirectory = !workingDirectoryInput ? undefined : 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 // custom script to run
const scriptInput = core.getInput('script', { required: true }); const scriptInput = core.getInput('script', { required: true });
const scripts = script_parser_1.parseScript(scriptInput); const scripts = script_parser_1.parseScript(scriptInput);
@@ -78,9 +107,9 @@ function run() {
console.log(`${script}`); console.log(`${script}`);
})); }));
// install SDK // install SDK
yield sdk_installer_1.installAndroidSdk(apiLevel, target, arch, emulatorBuild); yield sdk_installer_1.installAndroidSdk(apiLevel, target, arch, emulatorBuild, ndkVersion, cmakeVersion);
// launch an emulator // launch an emulator
yield emulator_manager_1.launchEmulator(apiLevel, target, arch, profile, emulatorOptions, disableAnimations); yield emulator_manager_1.launchEmulator(apiLevel, target, arch, profile, avdName, emulatorOptions, disableAnimations);
// execute the custom script // execute the custom script
try { try {
// move to custom working directory if set // move to custom working directory if set
+1
View File
@@ -1,5 +1,6 @@
"use strict"; "use strict";
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.parseScript = void 0;
/** /**
* Convert a (potentially multi-line) script to an array of single-line script(s). * Convert a (potentially multi-line) script to an array of single-line script(s).
*/ */
+66 -28
View File
@@ -1,53 +1,83 @@
"use strict"; "use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
return new (P || (P = Promise))(function (resolve, reject) { if (k2 === undefined) k2 = k;
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } }) : (function(o, m, k, k2) {
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } if (k2 === undefined) k2 = k;
step((generator = generator.apply(thisArg, _arguments || [])).next()); o[k2] = m[k];
}); }));
}; var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) { var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod; if (mod && mod.__esModule) return mod;
var result = {}; var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
result["default"] = mod; __setModuleDefault(result, mod);
return result; return result;
}; };
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
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) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.installAndroidSdk = void 0;
const core = __importStar(require("@actions/core")); const core = __importStar(require("@actions/core"));
const exec = __importStar(require("@actions/exec")); const exec = __importStar(require("@actions/exec"));
const BUILD_TOOLS_VERSION = '29.0.3'; const io = __importStar(require("@actions/io"));
const CMDLINE_TOOLS_URL_MAC = 'https://dl.google.com/android/repository/commandlinetools-linux-6200805_latest.zip'; const tc = __importStar(require("@actions/tool-cache"));
const CMDLINE_TOOLS_URL_LINUX = 'https://dl.google.com/android/repository/commandlinetools-linux-6200805_latest.zip'; const fs = __importStar(require("fs"));
const BUILD_TOOLS_VERSION = '30.0.2';
const CMDLINE_TOOLS_URL_MAC = 'https://dl.google.com/android/repository/commandlinetools-mac-6858069_latest.zip';
const CMDLINE_TOOLS_URL_LINUX = 'https://dl.google.com/android/repository/commandlinetools-linux-6858069_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, emulatorBuild) { function installAndroidSdk(apiLevel, target, arch, emulatorBuild, ndkVersion, cmakeVersion) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const isOnMac = process.platform === 'darwin'; 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) { if (!isOnMac) {
yield exec.exec(`sh -c \\"sudo chmod -R 777 ${process.env.ANDROID_HOME}"`); yield exec.exec(`sh -c \\"sudo chown $USER:$USER ${process.env.ANDROID_HOME} -R`);
yield exec.exec(`sh -c \\"echo -e '\n84831b9409646a918e30573bab4c9c91346d8abd' > ${process.env.ANDROID_HOME}/licenses/android-sdk-preview-license"`); }
const cmdlineToolsPath = `${process.env.ANDROID_HOME}/cmdline-tools`;
if (!fs.existsSync(cmdlineToolsPath)) {
console.log('Installing new cmdline-tools.');
const sdkUrl = isOnMac ? CMDLINE_TOOLS_URL_MAC : CMDLINE_TOOLS_URL_LINUX;
// await io.mkdirP(cmdlineToolsPath);
const downloadPath = yield tc.downloadTool(sdkUrl);
yield tc.extractZip(downloadPath, cmdlineToolsPath);
yield io.mv(`${cmdlineToolsPath}/cmdline-tools`, `${cmdlineToolsPath}/latest`);
yield exec.exec(`ls -la ${cmdlineToolsPath}`);
// add paths for commandline-tools and platform-tools
core.addPath(`${cmdlineToolsPath}/latest:${cmdlineToolsPath}/latest/bin:${process.env.ANDROID_HOME}/platform-tools`);
yield exec.exec(`ls -la ${cmdlineToolsPath}/latest`);
}
// additional permission and license requirements for Linux
const sdkPreviewLicensePath = `${process.env.ANDROID_HOME}/licenses/android-sdk-preview-license`;
if (!isOnMac && !fs.existsSync(sdkPreviewLicensePath)) {
fs.writeFileSync(sdkPreviewLicensePath, '\n84831b9409646a918e30573bab4c9c91346d8abd');
}
// license required for API 30 system images
const sdkArmDbtLicensePath = `${process.env.ANDROID_HOME}/licenses/android-sdk-arm-dbt-license`;
if (apiLevel == 30 && !fs.existsSync(sdkArmDbtLicensePath)) {
fs.writeFileSync(sdkArmDbtLicensePath, '\n859f317696f67ef3d7f30a50a5560e7834b43903');
} }
console.log('Installing latest build tools, platform tools, and platform.'); console.log('Installing latest build tools, platform tools, and platform.');
yield exec.exec(`sh -c \\"sdkmanager --install 'build-tools;${BUILD_TOOLS_VERSION}' platform-tools '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) { if (emulatorBuild) {
console.log(`Installing emulator build ${emulatorBuild}.`); console.log(`Installing emulator build ${emulatorBuild}.`);
yield exec.exec(`curl -fo emulator.zip https://dl.google.com/android/repository/emulator-${isOnMac ? 'darwin' : 'linux'}-${emulatorBuild}.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 io.rmRF(`${process.env.ANDROID_HOME}/emulator`);
yield exec.exec(`sudo unzip -q emulator.zip -d ${process.env.ANDROID_HOME}`); yield exec.exec(`unzip -q emulator.zip -d ${process.env.ANDROID_HOME}`);
yield exec.exec(`sudo rm -f emulator.zip`); yield io.rmRF('emulator.zip');
} }
else { else {
console.log('Installing latest emulator.'); console.log('Installing latest emulator.');
@@ -55,6 +85,14 @@ function installAndroidSdk(apiLevel, target, arch, emulatorBuild) {
} }
console.log('Installing system images.'); console.log('Installing system images.');
yield exec.exec(`sh -c \\"sdkmanager --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; exports.installAndroidSdk = installAndroidSdk;
+3306 -1429
View File
File diff suppressed because it is too large Load Diff
+16 -16
View File
@@ -21,24 +21,24 @@
"author": "Reactive Circus", "author": "Reactive Circus",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@actions/core": "^1.0.0", "@actions/core": "^1.2.6",
"@actions/exec": "^1.0.1", "@actions/exec": "^1.0.4",
"@actions/tool-cache": "^1.1.2", "@actions/tool-cache": "^1.6.0",
"minimist": "^1.2.5" "minimist": "^1.2.5"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^24.0.13", "@types/jest": "^24.9.1",
"@types/node": "^12.0.4", "@types/node": "^12.12.64",
"@typescript-eslint/eslint-plugin": "^2.6.1", "@typescript-eslint/eslint-plugin": "^2.34.0",
"@typescript-eslint/parser": "^2.6.1", "@typescript-eslint/parser": "^2.34.0",
"eslint": "^6.6.0", "eslint": "^6.8.0",
"eslint-config-prettier": "^6.5.0", "eslint-config-prettier": "^6.12.0",
"eslint-plugin-import": "^2.18.2", "eslint-plugin-import": "^2.22.1",
"eslint-plugin-prettier": "^3.1.1", "eslint-plugin-prettier": "^3.1.4",
"jest": "^25.2.3", "jest": "^25.5.4",
"jest-circus": "^25.2.3", "jest-circus": "^25.5.4",
"prettier": "^1.18.2", "prettier": "^1.19.1",
"ts-jest": "^24.0.2", "ts-jest": "^26.4.1",
"typescript": "^3.5.1" "typescript": "^3.9.7"
} }
} }
+4 -4
View File
@@ -5,14 +5,14 @@ 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.
*/ */
export async function launchEmulator(apiLevel: number, target: string, arch: string, profile: string, emulatorOptions: string, disableAnimations: boolean): Promise<void> { export async function launchEmulator(apiLevel: number, target: string, arch: string, profile: string, avdName: string, emulatorOptions: string, disableAnimations: boolean): Promise<void> {
// create a new AVD // create a new AVD
if (profile.trim() !== '') { if (profile.trim() !== '') {
console.log(`Creating AVD with custom profile ${profile}`); console.log(`Creating AVD with custom profile ${profile}`);
await exec.exec(`avdmanager 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 "${avdName}" --abi "${target}/${arch}" --package "system-images;android-${apiLevel};${target};${arch}" --device "${profile}"`);
} else { } else {
console.log(`Creating AVD without custom profile.`); console.log(`Creating AVD without custom profile.`);
await exec.exec(`sh -c \\"echo no | avdmanager 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 "${avdName}" --abi '${target}/${arch}' --package 'system-images;android-${apiLevel};${target};${arch}'"`);
} }
// start emulator // start emulator
@@ -23,7 +23,7 @@ export async function launchEmulator(apiLevel: number, target: string, arch: str
emulatorOptions += ' -accel off'; emulatorOptions += ' -accel off';
} }
await exec.exec(`sh -c \\"${process.env.ANDROID_HOME}/emulator/emulator -avd test ${emulatorOptions} &"`, [], { await exec.exec(`sh -c \\"${process.env.ANDROID_HOME}/emulator/emulator -avd "${avdName}" ${emulatorOptions} &"`, [], {
listeners: { listeners: {
stderr: (data: Buffer) => { stderr: (data: Buffer) => {
if (data.toString().includes('invalid command-line parameter')) { if (data.toString().includes('invalid command-line parameter')) {
+1 -1
View File
@@ -1,5 +1,5 @@
export const MIN_API_LEVEL = 15; export const MIN_API_LEVEL = 15;
export const VALID_TARGETS: Array<string> = ['default', 'google_apis']; export const VALID_TARGETS: Array<string> = ['default', 'google_apis', 'google_apis_playstore'];
export const VALID_ARCHS: Array<string> = ['x86', 'x86_64']; export const VALID_ARCHS: Array<string> = ['x86', 'x86_64'];
export function checkApiLevel(apiLevel: string): void { export function checkApiLevel(apiLevel: string): void {
+22 -3
View File
@@ -25,7 +25,8 @@ async function run() {
console.log(`API level: ${apiLevel}`); console.log(`API level: ${apiLevel}`);
// target of the system image // target of the system image
const target = core.getInput('target'); const targetInput = core.getInput('target');
const target = targetInput == 'playstore' ? 'google_apis_playstore' : targetInput;
checkTarget(target); checkTarget(target);
console.log(`target: ${target}`); console.log(`target: ${target}`);
@@ -38,6 +39,10 @@ async function run() {
const profile = core.getInput('profile'); const profile = core.getInput('profile');
console.log(`Hardware profile: ${profile}`); console.log(`Hardware profile: ${profile}`);
// custom name used for creating the AVD
const avdName = core.getInput('avd-name');
console.log(`AVD name: ${avdName}`);
// emulator options // emulator options
const emulatorOptions = core.getInput('emulator-options').trim(); const emulatorOptions = core.getInput('emulator-options').trim();
console.log(`emulator options: ${emulatorOptions}`); console.log(`emulator options: ${emulatorOptions}`);
@@ -63,6 +68,20 @@ async function run() {
} }
const workingDirectory = !workingDirectoryInput ? undefined : 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 // custom script to run
const scriptInput = core.getInput('script', { required: true }); const scriptInput = core.getInput('script', { required: true });
const scripts = parseScript(scriptInput); const scripts = parseScript(scriptInput);
@@ -72,10 +91,10 @@ async function run() {
}); });
// install SDK // install SDK
await installAndroidSdk(apiLevel, target, arch, emulatorBuild); await installAndroidSdk(apiLevel, target, arch, emulatorBuild, ndkVersion, cmakeVersion);
// launch an emulator // launch an emulator
await launchEmulator(apiLevel, target, arch, profile, emulatorOptions, disableAnimations); await launchEmulator(apiLevel, target, arch, profile, avdName, emulatorOptions, disableAnimations);
// execute the custom script // execute the custom script
try { try {
+48 -18
View File
@@ -1,30 +1,51 @@
import * as core from '@actions/core'; import * as core from '@actions/core';
import * as exec from '@actions/exec'; import * as exec from '@actions/exec';
import * as io from '@actions/io';
import * as tc from '@actions/tool-cache';
import * as fs from 'fs';
const BUILD_TOOLS_VERSION = '29.0.3'; const BUILD_TOOLS_VERSION = '30.0.2';
const CMDLINE_TOOLS_URL_MAC = 'https://dl.google.com/android/repository/commandlinetools-linux-6200805_latest.zip'; const CMDLINE_TOOLS_URL_MAC = 'https://dl.google.com/android/repository/commandlinetools-mac-6858069_latest.zip';
const CMDLINE_TOOLS_URL_LINUX = 'https://dl.google.com/android/repository/commandlinetools-linux-6200805_latest.zip'; const CMDLINE_TOOLS_URL_LINUX = 'https://dl.google.com/android/repository/commandlinetools-linux-6858069_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, emulatorBuild?: string): Promise<void> { export async function installAndroidSdk(apiLevel: number, target: string, arch: string, emulatorBuild?: string, ndkVersion?: string, cmakeVersion?: string): Promise<void> {
const isOnMac = process.platform === 'darwin'; 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 if (!isOnMac) {
core.addPath(`${process.env.ANDROID_HOME}/cmdline-tools/tools:${process.env.ANDROID_HOME}/cmdline-tools/tools/bin:${process.env.ANDROID_HOME}/platform-tools`); await exec.exec(`sh -c \\"sudo chown $USER:$USER ${process.env.ANDROID_HOME} -R`);
}
const cmdlineToolsPath = `${process.env.ANDROID_HOME}/cmdline-tools`;
if (!fs.existsSync(cmdlineToolsPath)) {
console.log('Installing new cmdline-tools.');
const sdkUrl = isOnMac ? CMDLINE_TOOLS_URL_MAC : CMDLINE_TOOLS_URL_LINUX;
// await io.mkdirP(cmdlineToolsPath);
const downloadPath = await tc.downloadTool(sdkUrl);
await tc.extractZip(downloadPath, cmdlineToolsPath);
await io.mv(`${cmdlineToolsPath}/cmdline-tools`, `${cmdlineToolsPath}/latest`);
await exec.exec(`ls -la ${cmdlineToolsPath}`);
// add paths for commandline-tools and platform-tools
core.addPath(`${cmdlineToolsPath}/latest:${cmdlineToolsPath}/latest/bin:${process.env.ANDROID_HOME}/platform-tools`);
await exec.exec(`ls -la ${cmdlineToolsPath}/latest`);
}
// additional permission and license requirements for Linux // additional permission and license requirements for Linux
if (!isOnMac) { const sdkPreviewLicensePath = `${process.env.ANDROID_HOME}/licenses/android-sdk-preview-license`;
await exec.exec(`sh -c \\"sudo chmod -R 777 ${process.env.ANDROID_HOME}"`); if (!isOnMac && !fs.existsSync(sdkPreviewLicensePath)) {
await exec.exec(`sh -c \\"echo -e '\n84831b9409646a918e30573bab4c9c91346d8abd' > ${process.env.ANDROID_HOME}/licenses/android-sdk-preview-license"`); fs.writeFileSync(sdkPreviewLicensePath, '\n84831b9409646a918e30573bab4c9c91346d8abd');
}
// license required for API 30 system images
const sdkArmDbtLicensePath = `${process.env.ANDROID_HOME}/licenses/android-sdk-arm-dbt-license`;
if (apiLevel == 30 && !fs.existsSync(sdkArmDbtLicensePath)) {
fs.writeFileSync(sdkArmDbtLicensePath, '\n859f317696f67ef3d7f30a50a5560e7834b43903');
} }
console.log('Installing latest build tools, platform tools, and platform.'); console.log('Installing latest build tools, platform tools, and platform.');
@@ -33,13 +54,22 @@ export async function installAndroidSdk(apiLevel: number, target: string, arch:
if (emulatorBuild) { if (emulatorBuild) {
console.log(`Installing emulator build ${emulatorBuild}.`); console.log(`Installing emulator build ${emulatorBuild}.`);
await exec.exec(`curl -fo emulator.zip https://dl.google.com/android/repository/emulator-${isOnMac ? 'darwin' : 'linux'}-${emulatorBuild}.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 io.rmRF(`${process.env.ANDROID_HOME}/emulator`);
await exec.exec(`sudo unzip -q emulator.zip -d ${process.env.ANDROID_HOME}`); await exec.exec(`unzip -q emulator.zip -d ${process.env.ANDROID_HOME}`);
await exec.exec(`sudo rm -f emulator.zip`); await io.rmRF('emulator.zip');
} else { } else {
console.log('Installing latest emulator.'); console.log('Installing latest emulator.');
await exec.exec(`sh -c \\"sdkmanager --install emulator > /dev/null"`); await exec.exec(`sh -c \\"sdkmanager --install emulator > /dev/null"`);
} }
console.log('Installing system images.'); console.log('Installing system images.');
await exec.exec(`sh -c \\"sdkmanager --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"`);
}
} }
+4 -4
View File
@@ -3,13 +3,13 @@ apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-android-extensions'
android { android {
compileSdkVersion 29 compileSdkVersion 30
buildToolsVersion "29.0.3" buildToolsVersion "30.0.2"
defaultConfig { defaultConfig {
applicationId "com.example.testapp" applicationId "com.example.testapp"
minSdkVersion 15 minSdkVersion 15
targetSdkVersion 29 targetSdkVersion 30
versionCode 1 versionCode 1
versionName "1.0" versionName "1.0"
@@ -28,7 +28,7 @@ dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.2.0' implementation 'androidx.core:core-ktx:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.13' testImplementation 'junit:junit:4.13'
androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.ext:junit:1.1.1'
+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.3.70' ext.kotlin_version = '1.4.0'
repositories { repositories {
google() google()
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.6.0' classpath 'com.android.tools.build:gradle:4.0.1'
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
View File
@@ -1,6 +1,7 @@
org.gradle.parallel=true org.gradle.parallel=true
org.gradle.configureondemand=true org.gradle.configureondemand=true
org.gradle.caching=true org.gradle.caching=true
org.gradle.vfs.watch=true
# Enable Kotlin incremental compilation # Enable Kotlin incremental compilation
kotlin.incremental=true kotlin.incremental=true
+1 -1
View File
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip