mirror of
https://github.com/reactivecircus/android-emulator-runner.git
synced 2026-09-01 01:59:32 +00:00
Compare commits
181 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e53cc7208d | |||
| dc1e458fb5 | |||
| 028694f45a | |||
| 088647b700 | |||
| 91d2d14e77 | |||
| ed009f5318 | |||
| 04335bcf9e | |||
| 660ac26f5b | |||
| cad321644d | |||
| ed6254cde0 | |||
| 8acdc1106b | |||
| a917366169 | |||
| ae2c4d7c48 | |||
| 9644b1a1f9 | |||
| 4fe4b1ae37 | |||
| 6fe36e1748 | |||
| 597aa7c21b | |||
| 38164a29c1 | |||
| 969c9896d3 | |||
| 29b35ed135 | |||
| 001613e856 | |||
| 937ab7eebc | |||
| 016d4d097a | |||
| f9bdb6d84e | |||
| b68ca169d6 | |||
| 66283c0319 | |||
| f2bf410054 | |||
| 62e6348453 | |||
| 450c4c9f73 | |||
| 2548f9f3fc | |||
| 595a4563df | |||
| a173d65200 | |||
| 50d5b10c01 | |||
| c77bfe7c0b | |||
| ead704cc5a | |||
| e800ffffe6 | |||
| 8c07710601 | |||
| 3a18f50216 | |||
| 1b985f4c7a | |||
| a3dcdb348b | |||
| 81f860b51a | |||
| e93b947104 | |||
| 4b0628e9f8 | |||
| ca77a1081f | |||
| 1fef06a960 | |||
| d8da5b6410 | |||
| a0c0e7216b | |||
| 9d5d6236f4 | |||
| bc2cd2aaea | |||
| 61a61f94a9 | |||
| 426fbd2820 | |||
| e1bd48ca1d | |||
| 1e33853aad | |||
| c479bb6c0f | |||
| 677db68893 | |||
| f9c2835191 | |||
| 1036f55734 | |||
| 5a481d20f1 | |||
| f72f505164 | |||
| fabc29122a | |||
| 2cfd145246 | |||
| e1bdfefaa7 | |||
| b35b1c6e69 | |||
| acd15a6841 | |||
| a2c97ef57f | |||
| 8c71f5ed72 | |||
| 743ec40c5b | |||
| bad4154821 | |||
| bdaf04916f | |||
| 8e947e5bd6 | |||
| e1248ae048 | |||
| 8d5c4be650 | |||
| ce60a72e42 | |||
| 3c9349d876 | |||
| 949d52fd17 | |||
| 87e972298f | |||
| ea00f72211 | |||
| a4845e8d39 | |||
| a47c099672 | |||
| 411cd10ea9 | |||
| 9320c5114f | |||
| 6fd58f95fc | |||
| 9559a79fb8 | |||
| a410b40679 | |||
| 4d64c5e6fa | |||
| 402c25e52e | |||
| cd41888947 | |||
| 7809d88d54 | |||
| a9c993651c | |||
| f1d9765a2a | |||
| eb6b0c0524 | |||
| f30ff064b1 | |||
| cc3dd8cbef | |||
| 689ee971a8 | |||
| 4ea6abdd76 | |||
| 280a99ecb8 | |||
| aacab61b72 | |||
| a24195edb9 | |||
| 20da7eafac | |||
| 08a23a6229 | |||
| 5bd7b4aaec | |||
| 1e2ef7249a | |||
| 3045dbfaba | |||
| 4e0dd2c343 | |||
| 62d97062b0 | |||
| e35fd713f4 | |||
| 5b2310fbf0 | |||
| d15e7110de | |||
| cc46524d3f | |||
| a86d743e55 | |||
| 066fd9b4b0 | |||
| 9216c92cd9 | |||
| 219361aa4a | |||
| d203cdc3fb | |||
| 8896ab3615 | |||
| 8f0c30aea2 | |||
| d1f5d2c017 | |||
| d0dfd56912 | |||
| 7f2c89a896 | |||
| 1424075cfd | |||
| 0bc3ba22b5 | |||
| acdb69812b | |||
| 511d462729 | |||
| d83fb3d4fb | |||
| 1427331a62 | |||
| 1ded27b164 | |||
| 217e44e3c5 | |||
| 31d8d4204a | |||
| a5bc682448 | |||
| 4d1a61ed38 | |||
| 1bf9f17bc6 | |||
| 5e487dbdfe | |||
| 08852ad83b | |||
| 25bcfbab74 | |||
| 0c3db9cd7a | |||
| eba1fbfb59 | |||
| 8a4333421e | |||
| 50ef0172a3 | |||
| fe99eb10f3 | |||
| 6fa269535d | |||
| 9b7af03883 | |||
| 854ed15ed0 | |||
| 98e515da59 | |||
| 9e81ae6235 | |||
| 6f33a98fa4 | |||
| e94dda8413 | |||
| 43421164e2 | |||
| a3d693d819 | |||
| 5e38ea2647 | |||
| 080a93d5e1 | |||
| 98e4e00ead | |||
| 751bef68f6 | |||
| bbf0887eb9 | |||
| 84b9418977 | |||
| a732f125bb | |||
| 5201a33a90 | |||
| cf46dc4c18 | |||
| f790770d0f | |||
| 505c899f12 | |||
| 0d47f0813d | |||
| 838986e303 | |||
| 27a0759d85 | |||
| d85239a9fb | |||
| 91b2b2f06f | |||
| 205fbe1b31 | |||
| efc0dae5fa | |||
| 4281471e5b | |||
| 7f22785663 | |||
| c3e261e13b | |||
| 434e949c3b | |||
| 0d8602fe47 | |||
| 28373d3d4f | |||
| 045b7d8c5e | |||
| b0f3c521a5 | |||
| 308aa234e1 | |||
| d4b3926c7d | |||
| 877a4c47c3 | |||
| 11559ab158 | |||
| b5ac8dfbfe | |||
| ad97647a61 | |||
| 4690ba2920 |
@@ -5,9 +5,6 @@
|
||||
"es6": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"prettier/@typescript-eslint",
|
||||
"plugin:prettier/recommended"
|
||||
],
|
||||
"globals": {
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
name: Validate action typings
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
validate-typings:
|
||||
runs-on: "ubuntu-latest"
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: krzema12/github-actions-typing@v2
|
||||
@@ -0,0 +1,132 @@
|
||||
name: Main workflow
|
||||
on:
|
||||
workflow_call:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
JAVA_TOOL_OPTIONS: -Xmx4g
|
||||
timeout-minutes: 15
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
api-level: 23
|
||||
target: default
|
||||
arch: x86
|
||||
- os: ubuntu-24.04
|
||||
api-level: 24
|
||||
target: playstore
|
||||
arch: x86
|
||||
- os: macos-15-intel
|
||||
api-level: 31
|
||||
target: default
|
||||
arch: x86_64
|
||||
- os: ubuntu-24.04
|
||||
api-level: 34
|
||||
target: aosp_atd
|
||||
arch: x86_64
|
||||
- os: ubuntu-latest
|
||||
api-level: 34-ext10
|
||||
target: android-automotive
|
||||
arch: x86_64
|
||||
system-image-api-level: 34-ext9
|
||||
- os: ubuntu-latest
|
||||
api-level: 35
|
||||
target: google_apis
|
||||
arch: x86_64
|
||||
- os: ubuntu-latest
|
||||
api-level: "37.1"
|
||||
target: google_apis_playstore_ps16k
|
||||
arch: x86_64
|
||||
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: build, test and lint
|
||||
run: |
|
||||
npm install
|
||||
npm run build
|
||||
npm run lint
|
||||
npm test
|
||||
|
||||
- uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: 25
|
||||
|
||||
- uses: actions/cache@v6
|
||||
id: avd-cache
|
||||
with:
|
||||
path: |
|
||||
~/.android/avd/*
|
||||
~/.android/adb*
|
||||
~/.android/debug.keystore
|
||||
key: avd-${{ matrix.api-level }}-${{ matrix.os }}-${{ matrix.target }}-${{ matrix.arch }}
|
||||
|
||||
- uses: gradle/actions/setup-gradle@v6
|
||||
|
||||
- name: assemble tests
|
||||
working-directory: test-fixture
|
||||
run: ./gradlew assembleAndroidTest
|
||||
|
||||
- name: enable KVM for linux runners
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
||||
sudo udevadm control --reload-rules
|
||||
sudo udevadm trigger --name-match=kvm
|
||||
|
||||
- name: run emulator to generate snapshot for caching
|
||||
if: steps.avd-cache.outputs.cache-hit != 'true'
|
||||
uses: ./
|
||||
with:
|
||||
api-level: ${{ matrix.api-level }}
|
||||
target: ${{ matrix.target }}
|
||||
arch: ${{ matrix.arch }}
|
||||
system-image-api-level: ${{ matrix.system-image-api-level }}
|
||||
profile: Galaxy Nexus
|
||||
cores: 2
|
||||
sdcard-path-or-size: 100M
|
||||
avd-name: test
|
||||
force-avd-creation: false
|
||||
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
|
||||
disable-animations: false
|
||||
working-directory: ./test-fixture/
|
||||
channel: canary
|
||||
script: echo "Generated AVD snapshot for caching."
|
||||
|
||||
- name: run action
|
||||
uses: ./
|
||||
with:
|
||||
api-level: ${{ matrix.api-level }}
|
||||
target: ${{ matrix.target }}
|
||||
arch: ${{ matrix.arch }}
|
||||
system-image-api-level: ${{ matrix.system-image-api-level }}
|
||||
profile: Galaxy Nexus
|
||||
cores: 2
|
||||
ram-size: 2048M
|
||||
sdcard-path-or-size: 100M
|
||||
avd-name: test
|
||||
force-avd-creation: false
|
||||
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
|
||||
disable-animations: true
|
||||
working-directory: ./test-fixture/
|
||||
channel: canary
|
||||
pre-emulator-launch-script: |
|
||||
echo "Running pre emulator launch script. Printing the working directory now:"
|
||||
pwd
|
||||
script: |
|
||||
echo $GITHUB_REPOSITORY
|
||||
adb devices
|
||||
./gradlew connectedDebugAndroidTest
|
||||
@@ -0,0 +1,79 @@
|
||||
name: Manually triggered workflow
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
os:
|
||||
description: 'OS'
|
||||
required: true
|
||||
default: 'ubuntu-latest'
|
||||
api-level:
|
||||
description: 'API level of the platform and system image (if not overridden with system-image-api-level input) - e.g. 33, 35-ext15, Baklava'
|
||||
required: true
|
||||
default: '34'
|
||||
system-image-api-level:
|
||||
description: 'API level of the system image - e.g. 34-ext10, 35-ext15'
|
||||
target:
|
||||
description: 'target of the system image - default, google_apis, google_apis_playstore, aosp_atd, google_atd, android-wear, android-wear-cn, android-tv, google-tv, android-automotive, android-automotive-playstore or android-desktop'
|
||||
required: true
|
||||
default: 'default'
|
||||
arch:
|
||||
description: 'CPU architecture of the system image - x86, x86_64 or arm64-v8a'
|
||||
default: 'x86_64'
|
||||
emulator-options:
|
||||
description: 'command-line options used when launching the emulator'
|
||||
default: '-no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim'
|
||||
emulator-build:
|
||||
description: 'build number of a specific version of the emulator binary to use'
|
||||
channel:
|
||||
description: 'Channel to download the SDK components from - `stable`, `beta`, `dev`, `canary`'
|
||||
default: 'stable'
|
||||
script:
|
||||
description: 'custom script to run - e.g. `./gradlew connectedCheck`'
|
||||
required: true
|
||||
default: './gradlew connectedDebugAndroidTest'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ${{ github.event.inputs.os }}
|
||||
env:
|
||||
JAVA_TOOL_OPTIONS: -Xmx4g
|
||||
timeout-minutes: 15
|
||||
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: build, test and lint
|
||||
run: |
|
||||
npm install
|
||||
npm run build
|
||||
npm run lint
|
||||
npm test
|
||||
|
||||
- uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: 25
|
||||
|
||||
- uses: gradle/actions/setup-gradle@v6
|
||||
|
||||
- name: enable KVM for linux runners
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
||||
sudo udevadm control --reload-rules
|
||||
sudo udevadm trigger --name-match=kvm
|
||||
|
||||
- name: run action
|
||||
uses: ./
|
||||
with:
|
||||
api-level: ${{ github.event.inputs.api-level }}
|
||||
target: ${{ github.event.inputs.target }}
|
||||
arch: ${{ github.event.inputs.arch }}
|
||||
system-image-api-level: ${{ github.event.inputs.system-image-api-level }}
|
||||
profile: Galaxy Nexus
|
||||
emulator-options: ${{ github.event.inputs.emulator-options }}
|
||||
emulator-build: ${{ github.event.inputs.emulator-build }}
|
||||
channel: ${{ github.event.inputs.channel }}
|
||||
working-directory: ./test-fixture/
|
||||
script: ${{ github.event.inputs.script }}
|
||||
@@ -0,0 +1,9 @@
|
||||
name: Re-Run Tests on PR Comment Workflow
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
rerun-tests-job:
|
||||
if: github.event.issue.pull_request && contains(github.event.comment.body, 'run tests') # if comment is created on a PR, can also use the syntax if: contains(github.event.comment.html_url, '/pull/')
|
||||
uses: ./.github/workflows/main.yml
|
||||
@@ -1,79 +0,0 @@
|
||||
name: Main workflow
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
JAVA_TOOL_OPTIONS: -Xmx4g
|
||||
timeout-minutes: 15
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-latest, ubuntu-latest]
|
||||
api-level: [16, 23, 29]
|
||||
target: [default, google_apis]
|
||||
exclude:
|
||||
- os: ubuntu-latest
|
||||
api-level: 23
|
||||
- os: ubuntu-latest
|
||||
api-level: 29
|
||||
- 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:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: validate gradle wrapper
|
||||
uses: gradle/wrapper-validation-action@v1
|
||||
|
||||
- name: build, test and lint
|
||||
run: |
|
||||
npm install
|
||||
npm run build
|
||||
npm run lint
|
||||
npm test
|
||||
|
||||
- name: Java 15
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
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
|
||||
uses: ./
|
||||
with:
|
||||
api-level: ${{ matrix.api-level }}
|
||||
target: ${{ matrix.target }}
|
||||
arch: x86
|
||||
profile: Nexus 6
|
||||
avd-name: test
|
||||
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
|
||||
+10
@@ -91,3 +91,13 @@ typings/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# Mac
|
||||
.DS_Store
|
||||
|
||||
# Vscode
|
||||
.vscode
|
||||
|
||||
# IntelliJ IDEA
|
||||
.idea
|
||||
*.iml
|
||||
|
||||
+172
@@ -1,5 +1,177 @@
|
||||
# Change Log
|
||||
|
||||
## Unreleased
|
||||
|
||||
* Update SDK command-line tools to `23.0`. - [#492](https://github.com/ReactiveCircus/android-emulator-runner/pull/492)
|
||||
|
||||
## v2.38.0
|
||||
|
||||
* Update SDK build tools to `37.0.0`. - [#475](https://github.com/ReactiveCircus/android-emulator-runner/pull/475)
|
||||
|
||||
## v2.37.0
|
||||
|
||||
* Update SDK build tools to `36.0.0`, update SDK command-line tools to `20.0`. - [#467](https://github.com/ReactiveCircus/android-emulator-runner/pull/467)
|
||||
* Run action on Node 24. - [#466](https://github.com/ReactiveCircus/android-emulator-runner/pull/466)
|
||||
* Update Node and npm packages. - [#466](https://github.com/ReactiveCircus/android-emulator-runner/pull/466)
|
||||
|
||||
## v2.36.0
|
||||
|
||||
* Remove `--abi` option to better support 16-KB-based system images (#456).
|
||||
|
||||
|
||||
## v2.35.0
|
||||
|
||||
* Optimize config.ini updates and efficiency improvements report (#436).
|
||||
* Fix `pre-emulator-launch-script` (#439).
|
||||
* Allow `google_apis_ps16k` as a valid target (#440).
|
||||
|
||||
## v2.34.0
|
||||
|
||||
* Allow arbitrary value for `api-level` such as `Baklava` and `35-ext15`. - [428](https://github.com/ReactiveCircus/android-emulator-runner/pull/428)
|
||||
* Add option to specify `system-image-api-level` to use a system image with a different API level than the platform. - [428](https://github.com/ReactiveCircus/android-emulator-runner/pull/428)
|
||||
* Support automotive and desktop targets: `android-automotive`, `android-automotive-playstore`, `android-desktop`. - [428](https://github.com/ReactiveCircus/android-emulator-runner/pull/428)
|
||||
|
||||
## v2.33.0
|
||||
|
||||
* Fix missing AVD directory in `ubuntu-24.04` runner. - [415](https://github.com/ReactiveCircus/android-emulator-runner/pull/415)
|
||||
* Update SDK build tools to `35.0.0`, update SDK command-line tools to `16.0`. - [#356](https://github.com/ReactiveCircus/android-emulator-runner/pull/356)
|
||||
* Stop changing owner of the SDK directory. - [406](https://github.com/ReactiveCircus/android-emulator-runner/pull/406) [#409](https://github.com/ReactiveCircus/android-emulator-runner/pull/409)
|
||||
* Upgrade to latest npm dependencies. - [#401](https://github.com/ReactiveCircus/android-emulator-runner/pull/401)
|
||||
|
||||
## v2.32.0
|
||||
|
||||
* Add `port` parameter for customizing the emulator port to use. - [#383](https://github.com/ReactiveCircus/android-emulator-runner/pull/383)
|
||||
|
||||
|
||||
## v2.31.0
|
||||
|
||||
* Support setting `VanillaIceCream` as `api-level`. - [#378](https://github.com/ReactiveCircus/android-emulator-runner/pull/378)
|
||||
* Install `platforms` for the specified `api-level`. - [#384](https://github.com/ReactiveCircus/android-emulator-runner/pull/384)
|
||||
|
||||
## v2.30.1
|
||||
|
||||
* Run action on Node 20. - [#371](https://github.com/ReactiveCircus/android-emulator-runner/pull/371)
|
||||
|
||||
## v2.30.0
|
||||
|
||||
* Update to Node 20. - [#369](https://github.com/ReactiveCircus/android-emulator-runner/pull/369)
|
||||
|
||||
## v2.29.0
|
||||
|
||||
* Fixed emulator download URL. - [#343](https://github.com/ReactiveCircus/android-emulator-runner/pull/343)
|
||||
* Upgrade to latest npm dependencies. - [#347](https://github.com/ReactiveCircus/android-emulator-runner/pull/347) [#355](https://github.com/ReactiveCircus/android-emulator-runner/pull/355)
|
||||
* Update SDK command-line tools to `11.0`. - [#356](https://github.com/ReactiveCircus/android-emulator-runner/pull/356)
|
||||
* Update SDK build tools to `34.0.0`. - [#356](https://github.com/ReactiveCircus/android-emulator-runner/pull/356)
|
||||
|
||||
## v2.28.0
|
||||
|
||||
* Add `emulator-boot-timeout` to support configuring maximum time waiting for emulator boot. - [#326](https://github.com/ReactiveCircus/android-emulator-runner/pull/326)
|
||||
* Support non-integer `api-level`. - [#317](https://github.com/ReactiveCircus/android-emulator-runner/pull/317)
|
||||
* Replace deprecated `ANDROID_SDK_ROOT` with `ANDROID_HOME`. - [304](https://github.com/ReactiveCircus/android-emulator-runner/pull/304)
|
||||
* Update SDK command-line tools to `9.0`. - [#331](https://github.com/ReactiveCircus/android-emulator-runner/pull/331)
|
||||
* Update SDK build tools to `33.0.2`. - [#331](https://github.com/ReactiveCircus/android-emulator-runner/pull/331)
|
||||
|
||||
## v2.27.0
|
||||
|
||||
* Added `pre-emulator-launch-script` to support running script after creating the AVD and before launching the emulator. - [#247](https://github.com/ReactiveCircus/android-emulator-runner/pull/247) @nilsreichardt.
|
||||
* Update to Node 16. - [#276](https://github.com/ReactiveCircus/android-emulator-runner/pull/276) @mattjohnsonpint.
|
||||
* Update NPM dependencies. - [#282](https://github.com/ReactiveCircus/android-emulator-runner/pull/282) @mattjohnsonpint.
|
||||
* Update README with more context on hardware acceleration on GitHub hosted runners. - [#279](https://github.com/ReactiveCircus/android-emulator-runner/pull/279) @mrk-han.
|
||||
|
||||
|
||||
## v2.26.0
|
||||
|
||||
* Support [github-actions-typing](https://github.com/krzema12/github-actions-typing). - [#257](https://github.com/ReactiveCircus/android-emulator-runner/pull/257) @LeoColman.
|
||||
|
||||
## v2.25.0
|
||||
|
||||
* Auto detect hardware acceleration on Linux. - [#254](https://github.com/ReactiveCircus/android-emulator-runner/pull/254) @notbigdata.
|
||||
* Update build tools to `33.0.0`.
|
||||
* Update SDK command-line tools to `7.0`.
|
||||
|
||||
## v2.24.0
|
||||
|
||||
* Add option to specify `heap-size` for the AVD. - [#245](https://github.com/ReactiveCircus/android-emulator-runner/pull/245) @timusus.
|
||||
|
||||
## v2.23.0
|
||||
|
||||
* Update build tools to `32.0.0`. - [#212](https://github.com/ReactiveCircus/android-emulator-runner/pull/212)
|
||||
* Update SDK command-line tools to `6.0`. - [#213](https://github.com/ReactiveCircus/android-emulator-runner/pull/213)
|
||||
* Add option to specify `disk-size` for the AVD. - [#219](https://github.com/ReactiveCircus/android-emulator-runner/pull/219) @ViliusSutkus89.
|
||||
* Improve logging by grouping log lines. - [#224](https://github.com/ReactiveCircus/android-emulator-runner/pull/224) @michaelkaye.
|
||||
|
||||
## v2.22.0
|
||||
|
||||
* Add option to enable hardware keyboard. - [#209](https://github.com/ReactiveCircus/android-emulator-runner/pull/209) (upstreamed from the [Doist fork](https://github.com/Doist/android-emulator-runner/commit/4b6ca99f0d657662beca3eb0c22d8e254fbd5b31)).
|
||||
* Update README and fix typos. - [#203](https://github.com/ReactiveCircus/android-emulator-runner/pull/203) @JPrendy.
|
||||
|
||||
## v2.21.0
|
||||
|
||||
* Support new [ATD](https://developer.android.com/studio/preview/features#gmd-atd) targets optimized to reduce CPU and memory resources when running instrumented tests: `aosp_atd`, `google_atd`. - [#198](https://github.com/ReactiveCircus/android-emulator-runner/pull/198). Note that currently these targets require the following:
|
||||
* `api-level: 30`
|
||||
* `arch: x86` or `arch: arm64-v8a`
|
||||
* `channel: canary`
|
||||
|
||||
## v2.20.0
|
||||
|
||||
* Support non-mobile targets: `android-wear`, `android-wear-cn`, `android-tv` and `google-tv`. - [#180](https://github.com/ReactiveCircus/android-emulator-runner/pull/180) @alexvanyo.
|
||||
* Update SDK command-line tools to `5.0`. - [#174](https://github.com/ReactiveCircus/android-emulator-runner/pull/174)
|
||||
* Update build tools to `31.0.0`. - [#174](https://github.com/ReactiveCircus/android-emulator-runner/pull/174)
|
||||
* Add option to specify the `channel` to download SDK components from: `stable` (default), `beta`, `dev` and `canary`. - [#185](https://github.com/ReactiveCircus/android-emulator-runner/pull/185)
|
||||
|
||||
## v2.19.1
|
||||
|
||||
* Accept all Android SDK Licenses to fix `sdkmanager` installation. - [#172](https://github.com/ReactiveCircus/android-emulator-runner/pull/172) @marcuspridham.
|
||||
|
||||
## v2.19.0
|
||||
|
||||
* Add option to specify `ram-size` for the AVD. - [#165](https://github.com/ReactiveCircus/android-emulator-runner/pull/165).
|
||||
|
||||
|
||||
## v2.18.1
|
||||
|
||||
* Added support for setting modern emulator build ids for `emulator-build`. Not all build ids are supported until we are able to figure out at which build id the download URL pattern changed. `7425822` (version 30.7.3) is currently the last known working build id.
|
||||
|
||||
## v2.18.0
|
||||
|
||||
* Add `force-avd-creation` which when set to `false` will skip avd creation if avd with same name exists. This enables AVD snapshot caching which can significantly reduce emulator startup time. See [README.md](https://github.com/ReactiveCircus/android-emulator-runner/blob/main/README.md#usage) for a sample workflow. - [#159](https://github.com/ReactiveCircus/android-emulator-runner/pull/159)
|
||||
|
||||
## v2.17.0
|
||||
|
||||
* Add option to toggle Linux hardware acceleration - [#154](https://github.com/ReactiveCircus/android-emulator-runner/pull/154) @stevestotter
|
||||
|
||||
## v2.16.0
|
||||
|
||||
* Avoid wrapping script code in quotes - [#134](https://github.com/ReactiveCircus/android-emulator-runner/pull/134) @hostilefork
|
||||
* Add option to disable spellcheck - [#143](https://github.com/ReactiveCircus/android-emulator-runner/pull/143) @AfzalivE
|
||||
* Add support for arm64-v8a for Apple Silicon Macs - [#146](https://github.com/ReactiveCircus/android-emulator-runner/pull/146) @Jeehut
|
||||
|
||||
## v2.15.0
|
||||
|
||||
* Added support for specifying the number of cores to use for the emulator - [#130](https://github.com/ReactiveCircus/android-emulator-runner/pull/130).
|
||||
|
||||
## v2.14.3
|
||||
|
||||
* Support `macos-11.0` (Big Sur) runner - [#124](https://github.com/ReactiveCircus/android-emulator-runner/pull/124).
|
||||
|
||||
## v2.14.2
|
||||
|
||||
* Support API 28 system images with `google_apis` or `google_apis_playstore` target - [#117](https://github.com/ReactiveCircus/android-emulator-runner/pull/117).
|
||||
|
||||
## v2.14.1
|
||||
|
||||
* Fix hang during AVD creation when `profile` is not specified - [#113](https://github.com/ReactiveCircus/android-emulator-runner/issues/113).
|
||||
|
||||
## v2.14.0
|
||||
|
||||
* Support specifying SD card path or size via `sdcard-path-or-size`.
|
||||
* Update npm packages.
|
||||
* Remove usages of deprecated `$ANDROID_HOME`.
|
||||
|
||||
## v2.13.0
|
||||
|
||||
* Updated to SDK command-line tools `3.0`.
|
||||
|
||||
## v2.12.0
|
||||
|
||||
Added support for using the `playstore` system images:
|
||||
|
||||
@@ -4,14 +4,33 @@
|
||||
<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 hardware-accelerated Android Emulators on macOS virtual machines.
|
||||
A GitHub Action for installing, configuring and running hardware-accelerated Android Emulators on Linux and 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 can be fast, but rely on two forms of hardware acceleration to reach their peak potential: [Graphics Acceleration](https://developer.android.com/studio/run/emulator-acceleration#accel-graphics), e.g. `emulator -gpu host` and [Virtual Machine(VM) Acceleration](https://developer.android.com/studio/run/emulator-acceleration#accel-vm), e.g. `emulator -accel on`. **Note:** GPU and VM Acceleration are two different and non-mutually exclusive forms of Hardware Acceleration.
|
||||
|
||||
The **macOS** VM provided by **GitHub Actions** has **HAXM** installed so we are able to create a new AVD instance, launch an emulator with hardware acceleration, and run our Android
|
||||
tests directly on the VM.
|
||||
This presents a challenge when running emulators on CI especially when running emulators within a docker container, because **Nested Virtualization** must be supported by the host VM which isn't the case for most cloud-based CI providers due to infrastructural limits. If you want to learn more about Emulators on CI, here's an article [Yang](https://github.com/ychescale9) wrote: [Running Android Instrumented Tests on CI](https://dev.to/ychescale9/running-android-emulators-on-ci-from-bitrise-io-to-github-actions-3j76).
|
||||
|
||||
This action automates the process by doing the following:
|
||||
## Running hardware accelerated emulators on Linux runners
|
||||
|
||||
GitHub's [larger Linux runners support running hardware accelerated emulators](https://github.blog/changelog/2023-02-23-hardware-accelerated-android-virtualization-on-actions-windows-and-linux-larger-hosted-runners/). It is now recommended to use the **Ubuntu** (`ubuntu-latest`) runners which are 2-3 times faster than the **macOS** ones which are also a lot more expensive. Remember to enable KVM in your workflow before running this action:
|
||||
|
||||
```
|
||||
- name: Enable KVM group perms
|
||||
run: |
|
||||
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
||||
sudo udevadm control --reload-rules
|
||||
sudo udevadm trigger --name-match=kvm
|
||||
```
|
||||
|
||||
## A note on VM Acceleration and why we don't need HAXM anymore
|
||||
|
||||
According to [this documentation](https://developer.android.com/studio/run/emulator-acceleration#vm-mac), "on Mac OS X v10.10 Yosemite and higher, the Android Emulator uses the built-in [Hypervisor.Framework](https://developer.apple.com/documentation/hypervisor) by default, and falls back to using Intel HAXM if Hypervisor.Framework fails to initialize." This means that **HAXM is only needed to achieve VM Acceleration if this default Hypervisor is not available on macOS machines.**
|
||||
|
||||
**Note**: Manually enabling and downloading HAXM is not recommended because it is redundant and not needed (see above), and for users of macOS 10.13 High Sierra and higher: macOS 10.13 [disables installation of kernel extensions by default](https://developer.apple.com/library/archive/technotes/tn2459/_index.html#//apple_ref/doc/uid/DTS40017658). Because Intel HAXM is a kernel extension, we would need to manually enable its installation on the base runner VM. Furthermore, manually trying to install HAXM on a Github Runner [brings up a popup](https://github.com/ReactiveCircus/android-emulator-runner/discussions/286#discussioncomment-4026120) which further hinders tests from running.
|
||||
|
||||
## Purpose
|
||||
|
||||
This action helps automate and configure the process of setting up an emulator and running your tests by doing the following:
|
||||
|
||||
- Install / update the required **Android SDK** components including `build-tools`, `platform-tools`, `platform` (for the required API level), `emulator` and `system-images` (for the required API level).
|
||||
- Create a new instance of **AVD** with the provided [configurations](#configurations).
|
||||
@@ -20,117 +39,239 @@ This action automates the process by doing the following:
|
||||
- Run a custom script provided by user once the Emulator is up and running - e.g. `./gradlew connectedCheck`.
|
||||
- Kill the Emulator and finish the action.
|
||||
|
||||
## Usage
|
||||
|
||||
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**.
|
||||
## Usage & Examples
|
||||
|
||||
A workflow that uses **android-emulator-runner** to run your instrumented tests on **API 29**:
|
||||
|
||||
```
|
||||
```yml
|
||||
jobs:
|
||||
test:
|
||||
runs-on: macos-latest
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run tests
|
||||
uses: reactivecircus/android-emulator-runner@v2
|
||||
with:
|
||||
api-level: 29
|
||||
script: ./gradlew connectedCheck
|
||||
- name: Enable KVM
|
||||
run: |
|
||||
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
||||
sudo udevadm control --reload-rules
|
||||
sudo udevadm trigger --name-match=kvm
|
||||
|
||||
- name: run tests
|
||||
uses: reactivecircus/android-emulator-runner@v2
|
||||
with:
|
||||
api-level: 29
|
||||
script: ./gradlew connectedCheck
|
||||
```
|
||||
|
||||
We can also leverage GitHub Actions's build matrix to test across multiple configurations:
|
||||
|
||||
```
|
||||
```yml
|
||||
jobs:
|
||||
test:
|
||||
runs-on: macos-latest
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
api-level: [21, 23, 29]
|
||||
target: [default, google_apis]
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run tests
|
||||
uses: reactivecircus/android-emulator-runner@v2
|
||||
with:
|
||||
api-level: ${{ matrix.api-level }}
|
||||
target: ${{ matrix.target }}
|
||||
arch: x86_64
|
||||
profile: Nexus 6
|
||||
script: ./gradlew connectedCheck
|
||||
- name: Enable KVM
|
||||
run: |
|
||||
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
||||
sudo udevadm control --reload-rules
|
||||
sudo udevadm trigger --name-match=kvm
|
||||
|
||||
- name: run tests
|
||||
uses: reactivecircus/android-emulator-runner@v2
|
||||
with:
|
||||
api-level: ${{ matrix.api-level }}
|
||||
target: ${{ matrix.target }}
|
||||
arch: x86_64
|
||||
profile: pixel_7_pro
|
||||
script: ./gradlew connectedCheck
|
||||
```
|
||||
|
||||
If you need specific versions of **NDK** and **CMake** installed:
|
||||
|
||||
```
|
||||
```yml
|
||||
jobs:
|
||||
test:
|
||||
runs-on: macos-latest
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- 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
|
||||
- name: Enable KVM
|
||||
run: |
|
||||
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
||||
sudo udevadm control --reload-rules
|
||||
sudo udevadm trigger --name-match=kvm
|
||||
|
||||
- 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
|
||||
```
|
||||
|
||||
If you need a specific [SDK Extensions](https://developer.android.com/guide/sdk-extensions) for the system image but not the platform:
|
||||
|
||||
```yml
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Enable KVM
|
||||
run: |
|
||||
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
||||
sudo udevadm control --reload-rules
|
||||
sudo udevadm trigger --name-match=kvm
|
||||
|
||||
- name: run tests
|
||||
uses: reactivecircus/android-emulator-runner@v2
|
||||
with:
|
||||
api-level: 34
|
||||
system-image-api-level: 34-ext9
|
||||
target: android-automotive
|
||||
script: ./gradlew connectedCheck
|
||||
```
|
||||
|
||||
We can significantly reduce emulator startup time by setting up AVD snapshot caching:
|
||||
|
||||
1. add a `gradle/actions/setup-gradle@v5` step for caching Gradle, more details see [#229](https://github.com/ReactiveCircus/android-emulator-runner/issues/229)
|
||||
2. add an `actions/cache@v5` step for caching the `avd`
|
||||
3. add a `reactivecircus/android-emulator-runner@v2` step to generate a clean snapshot - specify `emulator-options` without `no-snapshot`
|
||||
4. add another `reactivecircus/android-emulator-runner@v2` step to run your tests using existing AVD / snapshot - specify `emulator-options` with `no-snapshot-save`
|
||||
|
||||
```yml
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
api-level: [21, 23, 29]
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Enable KVM
|
||||
run: |
|
||||
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
||||
sudo udevadm control --reload-rules
|
||||
sudo udevadm trigger --name-match=kvm
|
||||
|
||||
- name: Gradle cache
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
|
||||
- name: AVD cache
|
||||
uses: actions/cache@v5
|
||||
id: avd-cache
|
||||
with:
|
||||
path: |
|
||||
~/.android/avd/*
|
||||
~/.android/adb*
|
||||
key: avd-${{ matrix.api-level }}
|
||||
|
||||
- name: create AVD and generate snapshot for caching
|
||||
if: steps.avd-cache.outputs.cache-hit != 'true'
|
||||
uses: reactivecircus/android-emulator-runner@v2
|
||||
with:
|
||||
api-level: ${{ matrix.api-level }}
|
||||
force-avd-creation: false
|
||||
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
|
||||
disable-animations: false
|
||||
script: echo "Generated AVD snapshot for caching."
|
||||
|
||||
- name: run tests
|
||||
uses: reactivecircus/android-emulator-runner@v2
|
||||
with:
|
||||
api-level: ${{ matrix.api-level }}
|
||||
force-avd-creation: false
|
||||
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
|
||||
disable-animations: true
|
||||
script: ./gradlew connectedCheck
|
||||
```
|
||||
|
||||
## Configurations
|
||||
|
||||
| **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**. |
|
||||
| `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+. |
|
||||
| `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". |
|
||||
| `api-level` | Required | N/A | API level of the platform and system image - e.g. `23`, `33`, `35-ext15`, `Baklava`. **Minimum API level supported is 15**. |
|
||||
| `system-image-api-level` | Optional | same as `api-level` | API level of the system image - e.g. `34-ext10`, `35-ext15`. |
|
||||
| `target` | Optional | `default` | Target of the system image - e.g. `default`, `google_apis`, `google_apis_ps16k`, `google_apis_playstore`, `google_apis_playstore_ps16k`, `android-wear`, `android-wear-cn`, `android-tv`, `google-tv`, `aosp_atd`, `google_atd`, `android-automotive`, `android-automotive-playstore`, `android-desktop`. Please run `sdkmanager --list` to see the available targets. |
|
||||
| `arch` | Optional | `x86` | CPU architecture of the system image - `x86`, `x86_64` or `arm64-v8a`. Note that `x86_64` image is only available for API 21+. `arm64-v8a` images require Android 4.2+ and are limited to fewer API levels (e.g. 30). |
|
||||
| `profile` | Optional | N/A | Hardware profile id used for creating the AVD - e.g. `pixel_7_pro`. For a list of all profiles available, run `avdmanager list device`. |
|
||||
| `cores` | Optional | 2 | Number of cores to use for the emulator (`hw.cpu.ncore` in config.ini). |
|
||||
| `ram-size` | Optional | N/A | Size of RAM to use for this AVD, in KB or MB, denoted with K or M. - e.g. `2048M` |
|
||||
| `heap-size` | Optional | N/A | Heap size to use for this AVD, in KB or MB, denoted with K or M. - e.g. `512M` |
|
||||
| `sdcard-path-or-size` | Optional | N/A | Path to the SD card image for this AVD or the size of a new SD card image to create for this AVD, in KB or MB, denoted with K or M. - e.g. `path/to/sdcard`, or `1000M`. |
|
||||
| `disk-size` | Optional | N/A | Disk size, or partition size to use for this AVD. Either in bytes or KB, MB or GB, when denoted with K, M or G. - e.g. `2048M` |
|
||||
| `avd-name` | Optional | `test` | Custom AVD name used for creating the Android Virtual Device. |
|
||||
| `force-avd-creation` | Optional | `true` | Whether to force create the AVD by overwriting an existing AVD with the same name as `avd-name` - `true` or `false`. |
|
||||
| `emulator-boot-timeout` | Optional | `600` | Emulator boot timeout in seconds. If it takes longer to boot, the action would fail - e.g. `300` for 5 minutes. |
|
||||
| `emulator-port` | Optional | `5554` | Emulator port to use. Allows to run this action on multiple workers on a single machine at the same time. This input is available for the script as `EMULATOR_PORT` enviromental variable. This port is automatically used by android device related tasks in gradle |
|
||||
| `emulator-options` | Optional | See below | Command-line options used when launching the emulator (replacing all default options) - e.g. `-no-window -no-snapshot -camera-back emulated`. |
|
||||
| `disable-animations` | Optional | `true` | Whether to disable animations - `true` or `false`. |
|
||||
| `disable-spellchecker` | Optional | `false` | Whether to disable spellchecker - `true` or `false`. |
|
||||
| `disable-linux-hw-accel` | Optional | `auto` | Whether to disable hardware acceleration on Linux machines - `true`, `false` or `auto`.|
|
||||
| `enable-hw-keyboard` | Optional | `false` | Whether to enable hardware keyboard - `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. |
|
||||
| `working-directory` | Optional | `./` | A custom working directory - e.g. `./android` if your root Gradle project is under the `./android` sub-directory within your repository. Will be used for `script` & `pre-emulator-launch-script`. |
|
||||
| `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` |
|
||||
| `channel` | Optional | stable | Channel to download the SDK components from - `stable`, `beta`, `dev`, `canary` |
|
||||
| `script` | Required | N/A | Custom script to run - e.g. to run Android instrumented tests on the emulator: `./gradlew connectedCheck` |
|
||||
| `pre-emulator-launch-script` | Optional | N/A | Custom script to run after creating the AVD and before launching the emulator - e.g. `./adjust-emulator-configs.sh` |
|
||||
|
||||
Default `emulator-options`: `-no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim`.
|
||||
|
||||
## 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/)
|
||||
- [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-kotlin](https://github.com/square/workflow-kotlin/tree/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)
|
||||
- [android/compose-samples](https://github.com/android/compose-samples/tree/main/.github/workflows)
|
||||
- [ReactiveCircus/streamlined](https://github.com/ReactiveCircus/streamlined/tree/main/.github/workflows)
|
||||
- [ReactiveCircus/FlowBinding](https://github.com/ReactiveCircus/FlowBinding)
|
||||
- [ReactiveCircus/FlowBinding](https://github.com/ReactiveCircus/FlowBinding/tree/main/.github/workflows)
|
||||
- [JakeWharton/RxBinding](https://github.com/JakeWharton/RxBinding/tree/master/.github/workflows)
|
||||
- [vinaygaba/Learn-Jetpack-Compose-By-Example](https://github.com/vinaygaba/Learn-Jetpack-Compose-By-Example/)
|
||||
- [vinaygaba/Learn-Jetpack-Compose-By-Example](https://github.com/vinaygaba/Learn-Jetpack-Compose-By-Example/tree/master/.github/workflows)
|
||||
- [ashishb/adb-enhanced](https://github.com/ashishb/adb-enhanced/tree/master/.github/workflows)
|
||||
- [vgaidarji/ci-matters](https://github.com/vgaidarji/ci-matters/blob/master/.github/workflows/main.yaml)
|
||||
- [simpledotorg/simple-android](https://github.com/simpledotorg/simple-android)
|
||||
- [simpledotorg/simple-android](https://github.com/simpledotorg/simple-android/tree/master/.github/workflows)
|
||||
- [cashapp/copper](https://github.com/cashapp/copper/blob/trunk/.github/workflows/build.yaml)
|
||||
- [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)
|
||||
- [square/leakcanary](https://github.com/square/leakcanary/tree/main/.github/workflows)
|
||||
- [hash-checker/hash-checker](https://github.com/hash-checker/hash-checker/tree/master/.github/workflows)
|
||||
- [hash-checker/hash-checker-lite](https://github.com/hash-checker/hash-checker-lite/tree/master/.github/workflows)
|
||||
- [Kiwix/kiwix-android](https://github.com/kiwix/kiwix-android/blob/develop/.github/workflows)
|
||||
- [wikimedia/apps-android-wikipedia](https://github.com/wikimedia/apps-android-wikipedia/blob/main/.github/workflows)
|
||||
- [google/android-fhir](https://github.com/google/android-fhir/tree/master/.github/workflows)
|
||||
- [google/accompanist](https://github.com/google/accompanist/blob/main/.github/workflows)
|
||||
- [dotanuki-labs/norris](https://github.com/dotanuki-labs/norris/blob/master/.github/workflows/main.yml)
|
||||
- [tinylog-org/tinylog](https://github.com/tinylog-org/tinylog/blob/v3.0/.github/workflows/build.yaml)
|
||||
- [hzi-braunschweig/SORMAS-Project](https://github.com/hzi-braunschweig/SORMAS-Project/blob/development/.github/workflows/sormas_app_ci.yml)
|
||||
- [ACRA/acra](https://github.com/ACRA/acra/blob/master/.github/workflows/test.yml)
|
||||
- [bitfireAT/davx5-ose](https://github.com/bitfireAT/davx5-ose/blob/dev-ose/.github/workflows/test-dev.yml)
|
||||
- [robolectric/robolectric](https://github.com/robolectric/robolectric/blob/master/.github/workflows/tests.yml)
|
||||
- [home-assistant/android](https://github.com/home-assistant/android/blob/master/.github/workflows/pr.yml)
|
||||
- [composablehorizons/compose-unstyled](https://github.com/composablehorizons/compose-unstyled/tree/main/.github/workflows)
|
||||
- [f3d-app/f3d-android](https://github.com/f3d-app/f3d-android/blob/master/.github/workflows/ci.yml)
|
||||
- [freeorion/freeorion](https://github.com/freeorion/freeorion/blob/master/.github/workflows/_build-android.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.
|
||||
If you are using **Android Emulator Runner** and want your project included in the list, please feel free to open a pull request.
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
# GitHub Action - Android Emulator Runner
|
||||
|
||||
Refer to the [recommendeations for versioning and releasing actions](https://github.com/actions/toolkit/blob/master/docs/action-versioning.md#recommendations).
|
||||
Refer to the [recommendations for versioning and releasing actions](https://github.com/actions/toolkit/blob/master/docs/action-versioning.md#recommendations).
|
||||
|
||||
## New major release
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import * as mapper from '../src/channel-id-mapper';
|
||||
|
||||
describe('channel id mapper tests', () => {
|
||||
it('Throws if channelName is unknown', () => {
|
||||
const func = () => {
|
||||
mapper.getChannelId('unknown-channel');
|
||||
};
|
||||
expect(func).toThrowError(`Unexpected channel name: 'unknown-channel'.`);
|
||||
});
|
||||
|
||||
it('Returns expected channelId from channelName', () => {
|
||||
expect(mapper.getChannelId('stable')).toBe(0);
|
||||
expect(mapper.getChannelId('beta')).toBe(1);
|
||||
expect(mapper.getChannelId('dev')).toBe(2);
|
||||
expect(mapper.getChannelId('canary')).toBe(3);
|
||||
});
|
||||
});
|
||||
@@ -1,65 +1,5 @@
|
||||
import * as validator from '../src/input-validator';
|
||||
|
||||
describe('api-level validator tests', () => {
|
||||
it('Throws if api-level is not a number', () => {
|
||||
const func = () => {
|
||||
validator.checkApiLevel('api');
|
||||
};
|
||||
expect(func).toThrowError(`Unexpected API level: 'api'.`);
|
||||
});
|
||||
|
||||
it('Throws if api-level is not an integer', () => {
|
||||
const func = () => {
|
||||
validator.checkApiLevel('29.1');
|
||||
};
|
||||
expect(func).toThrowError(`Unexpected API level: '29.1'.`);
|
||||
});
|
||||
|
||||
it('Throws if api-level is lower than min API supported', () => {
|
||||
const func = () => {
|
||||
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('15');
|
||||
};
|
||||
expect(func1).not.toThrow();
|
||||
|
||||
const func2 = () => {
|
||||
validator.checkApiLevel('29');
|
||||
};
|
||||
expect(func2).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('target validator tests', () => {
|
||||
it('Throws if target is unknown', () => {
|
||||
const func = () => {
|
||||
validator.checkTarget('some-target');
|
||||
};
|
||||
expect(func).toThrowError(`Value for input.target 'some-target' is unknown. Supported options: ${validator.VALID_TARGETS}`);
|
||||
});
|
||||
|
||||
it('Validates successfully with valid target', () => {
|
||||
const func1 = () => {
|
||||
validator.checkTarget('default');
|
||||
};
|
||||
expect(func1).not.toThrow();
|
||||
|
||||
const func2 = () => {
|
||||
validator.checkTarget('google_apis');
|
||||
};
|
||||
expect(func2).not.toThrow();
|
||||
|
||||
const func3 = () => {
|
||||
validator.checkTarget('google_apis');
|
||||
};
|
||||
expect(func3).not.toThrow();
|
||||
});
|
||||
});
|
||||
import { MAX_PORT, MIN_PORT } from '../src/input-validator';
|
||||
|
||||
describe('arch validator tests', () => {
|
||||
it('Throws if arch is unknown', () => {
|
||||
@@ -82,6 +22,85 @@ describe('arch validator tests', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('channel validator tests', () => {
|
||||
it('Throws if channel is unknown', () => {
|
||||
const func = () => {
|
||||
validator.checkChannel('some-channel');
|
||||
};
|
||||
expect(func).toThrowError(`Value for input.channel 'some-channel' is unknown. Supported options: ${validator.VALID_CHANNELS}`);
|
||||
});
|
||||
|
||||
it('Validates successfully with valid channel', () => {
|
||||
const func1 = () => {
|
||||
validator.checkChannel('stable');
|
||||
};
|
||||
expect(func1).not.toThrow();
|
||||
|
||||
const func2 = () => {
|
||||
validator.checkChannel('beta');
|
||||
};
|
||||
expect(func2).not.toThrow();
|
||||
|
||||
const func3 = () => {
|
||||
validator.checkChannel('dev');
|
||||
};
|
||||
expect(func3).not.toThrow();
|
||||
|
||||
const func4 = () => {
|
||||
validator.checkChannel('canary');
|
||||
};
|
||||
expect(func4).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('force-avd-creation validator tests', () => {
|
||||
it('Throws if force-avd-creation is not a boolean', () => {
|
||||
const func = () => {
|
||||
validator.checkForceAvdCreation('yes');
|
||||
};
|
||||
expect(func).toThrowError(`Input for input.force-avd-creation should be either 'true' or 'false'.`);
|
||||
});
|
||||
|
||||
it('Validates successfully if force-avd-creation is either true or false', () => {
|
||||
const func1 = () => {
|
||||
validator.checkForceAvdCreation('true');
|
||||
};
|
||||
expect(func1).not.toThrow();
|
||||
|
||||
const func2 = () => {
|
||||
validator.checkForceAvdCreation('false');
|
||||
};
|
||||
expect(func2).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('emulator-port validator tests', () => {
|
||||
it('Validates if emulator-port is even and in range', () => {
|
||||
const func = () => {
|
||||
validator.checkPort(5554);
|
||||
};
|
||||
expect(func).not.toThrow();
|
||||
});
|
||||
it('Throws if emulator-port is lower than MIN_PORT', () => {
|
||||
const func = () => {
|
||||
validator.checkPort(MIN_PORT - 2);
|
||||
};
|
||||
expect(func).toThrow();
|
||||
});
|
||||
it('Throws if emulator-port is higher than MAX_PORT', () => {
|
||||
const func = () => {
|
||||
validator.checkPort(MAX_PORT + 2);
|
||||
};
|
||||
expect(func).toThrow();
|
||||
});
|
||||
it('Throws if emulator-port is odd', () => {
|
||||
const func = () => {
|
||||
validator.checkPort(5555);
|
||||
};
|
||||
expect(func).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('disable-animations validator tests', () => {
|
||||
it('Throws if disable-animations is not a boolean', () => {
|
||||
const func = () => {
|
||||
@@ -103,6 +122,74 @@ describe('disable-animations validator tests', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('disable-spellchecker validator tests', () => {
|
||||
it('Throws if disable-spellchecker is not a boolean', () => {
|
||||
const func = () => {
|
||||
validator.checkDisableSpellchecker('yes');
|
||||
};
|
||||
expect(func).toThrowError(`Input for input.disable-spellchecker should be either 'true' or 'false'.`);
|
||||
});
|
||||
|
||||
it('Validates successfully if disable-spellchecker is either true or false', () => {
|
||||
const func1 = () => {
|
||||
validator.checkDisableSpellchecker('true');
|
||||
};
|
||||
expect(func1).not.toThrow();
|
||||
|
||||
const func2 = () => {
|
||||
validator.checkDisableSpellchecker('false');
|
||||
};
|
||||
expect(func2).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('disable-linux-hw-accel validator tests', () => {
|
||||
it('Throws if disable-linux-hw-accel is not a boolean', () => {
|
||||
const func = () => {
|
||||
validator.checkDisableLinuxHardwareAcceleration('yes');
|
||||
};
|
||||
expect(func).toThrowError(`Input for input.disable-linux-hw-accel should be either 'true' or 'false' or 'auto'.`);
|
||||
});
|
||||
|
||||
it('Validates successfully if disable-linux-hw-accel is either true or false or auto', () => {
|
||||
const func1 = () => {
|
||||
validator.checkDisableLinuxHardwareAcceleration('true');
|
||||
};
|
||||
expect(func1).not.toThrow();
|
||||
|
||||
const func2 = () => {
|
||||
validator.checkDisableLinuxHardwareAcceleration('false');
|
||||
};
|
||||
expect(func2).not.toThrow();
|
||||
|
||||
const func3 = () => {
|
||||
validator.checkDisableLinuxHardwareAcceleration('auto');
|
||||
};
|
||||
expect(func3).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('enable-hw-keyboard validator tests', () => {
|
||||
it('Throws if enable-hw-keyboard is not a boolean', () => {
|
||||
const func = () => {
|
||||
validator.checkEnableHardwareKeyboard('yes');
|
||||
};
|
||||
expect(func).toThrowError(`Input for input.enable-hw-keyboard should be either 'true' or 'false'.`);
|
||||
});
|
||||
|
||||
it('Validates successfully if enable-hardware-keyboard is either true or false', () => {
|
||||
const func1 = () => {
|
||||
validator.checkEnableHardwareKeyboard('true');
|
||||
};
|
||||
expect(func1).not.toThrow();
|
||||
|
||||
const func2 = () => {
|
||||
validator.checkEnableHardwareKeyboard('false');
|
||||
};
|
||||
expect(func2).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('emulator-build validator tests', () => {
|
||||
it('Throws if emulator-build is not a number', () => {
|
||||
const func = () => {
|
||||
@@ -125,3 +212,54 @@ describe('emulator-build validator tests', () => {
|
||||
expect(func).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('checkDiskSize validator tests', () => {
|
||||
it('Empty size is acceptable, means default', () => {
|
||||
const func = () => {
|
||||
validator.checkDiskSize('');
|
||||
};
|
||||
expect(func).not.toThrow();
|
||||
});
|
||||
|
||||
it('Numbers means bytes', () => {
|
||||
expect(() => {
|
||||
validator.checkDiskSize('8000000000');
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
it('Uppercase size modifier', () => {
|
||||
expect(() => {
|
||||
validator.checkDiskSize('8000000K');
|
||||
}).not.toThrow();
|
||||
expect(() => {
|
||||
validator.checkDiskSize('8000M');
|
||||
}).not.toThrow();
|
||||
expect(() => {
|
||||
validator.checkDiskSize('8G');
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
it('Lowercase size modifier', () => {
|
||||
expect(() => {
|
||||
validator.checkDiskSize('8000000k');
|
||||
}).not.toThrow();
|
||||
expect(() => {
|
||||
validator.checkDiskSize('8000m');
|
||||
}).not.toThrow();
|
||||
expect(() => {
|
||||
validator.checkDiskSize('8g');
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
it('Modifier without a number is unacceptable', () => {
|
||||
expect(() => {
|
||||
validator.checkDiskSize('G');
|
||||
}).toThrowError(`Unexpected disk size: 'G'.`);
|
||||
});
|
||||
|
||||
it('Double modifier is unacceptable', () => {
|
||||
expect(() => {
|
||||
validator.checkDiskSize('14gg');
|
||||
}).toThrowError(`Unexpected disk size: '14gg'.`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
inputs:
|
||||
api-level:
|
||||
type: string
|
||||
system-image-api-level:
|
||||
type: string
|
||||
target:
|
||||
type: enum
|
||||
allowed-values:
|
||||
- default
|
||||
- google_apis
|
||||
- google_apis_playstore
|
||||
- aosp_atd
|
||||
- google_atd
|
||||
- android-wear
|
||||
- android-wear-cn
|
||||
- android-tv
|
||||
- google-tv
|
||||
- android-automotive
|
||||
- android-automotive-playstore
|
||||
- android-desktop
|
||||
arch:
|
||||
type: enum
|
||||
allowed-values:
|
||||
- x86
|
||||
- x86_64
|
||||
- arm64-v8a
|
||||
profile:
|
||||
type: string
|
||||
cores:
|
||||
type: integer
|
||||
ram-size:
|
||||
type: string
|
||||
heap-size:
|
||||
type: string
|
||||
sdcard-path-or-size:
|
||||
type: string
|
||||
disk-size:
|
||||
type: string
|
||||
avd-name:
|
||||
type: string
|
||||
force-avd-creation:
|
||||
type: boolean
|
||||
emulator-boot-timeout:
|
||||
type: integer
|
||||
emulator-port:
|
||||
type: integer
|
||||
emulator-options:
|
||||
type: string
|
||||
disable-animations:
|
||||
type: boolean
|
||||
disable-spellchecker:
|
||||
type: boolean
|
||||
disable-linux-hw-accel:
|
||||
type: string
|
||||
enable-hw-keyboard:
|
||||
type: boolean
|
||||
emulator-build:
|
||||
type: string
|
||||
working-directory:
|
||||
type: string
|
||||
ndk:
|
||||
type: string
|
||||
cmake:
|
||||
type: string
|
||||
channel:
|
||||
type: enum
|
||||
allowed-values:
|
||||
- stable
|
||||
- beta
|
||||
- dev
|
||||
- canary
|
||||
script:
|
||||
type: string
|
||||
pre-emulator-launch-script:
|
||||
type: string
|
||||
+43
-6
@@ -1,30 +1,62 @@
|
||||
name: 'Android Emulator Runner'
|
||||
description: 'Installs, configures and starts an Android Emulator directly on macOS virtual machines.'
|
||||
description: 'Installs, configures and starts an Android Emulator directly on hardware-accelerated runners.'
|
||||
author: 'Reactive Circus'
|
||||
branding:
|
||||
icon: 'smartphone'
|
||||
icon: 'smartphone'
|
||||
color: 'green'
|
||||
inputs:
|
||||
api-level:
|
||||
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, 33, 35-ext15, Baklava'
|
||||
required: true
|
||||
system-image-api-level:
|
||||
description: 'API level of the system image - e.g. 34-ext10, 35-ext15. If not set the `api-level` input will be used.'
|
||||
required: false
|
||||
target:
|
||||
description: 'target of the system image - default, google_apis or playstore'
|
||||
description: 'target of the system image - e.g. default, google_apis, google_apis_ps16k, google_apis_playstore, google_apis_playstore_16k, aosp_atd, google_atd, android-wear, android-wear-cn, android-tv, google-tv, android-automotive, android-automotive-playstore or android-desktop'
|
||||
default: 'default'
|
||||
arch:
|
||||
description: 'CPU architecture of the system image - x86 or x86_64'
|
||||
description: 'CPU architecture of the system image - x86, x86_64 or arm64-v8a'
|
||||
default: 'x86'
|
||||
profile:
|
||||
description: 'hardware profile used for creating the AVD - e.g. `Nexus 6`'
|
||||
cores:
|
||||
description: 'the number of cores to use for the emulator'
|
||||
default: 2
|
||||
ram-size:
|
||||
description: 'size of RAM to use for this AVD, in KB or MB, denoted with K or M. - e.g. `2048M`'
|
||||
heap-size:
|
||||
description: 'size of heap to use for this AVD in MB. - e.g. `512M`'
|
||||
sdcard-path-or-size:
|
||||
description: 'path to the SD card image for this AVD or the size of a new SD card image to create for this AVD, in KB or MB, denoted with K or M. - e.g. `path/to/sdcard`, or `1000M`'
|
||||
disk-size:
|
||||
description: 'disk size to use for this AVD. Either in bytes or KB, MB or GB, when denoted with K, M or G'
|
||||
avd-name:
|
||||
description: 'custom AVD name used for creating the Android Virtual Device'
|
||||
default: 'test'
|
||||
force-avd-creation:
|
||||
description: 'whether to force create the AVD by overwriting an existing AVD with the same name as `avd-name` - `true` or `false`'
|
||||
default: 'true'
|
||||
emulator-boot-timeout:
|
||||
description: 'Emulator boot timeout in seconds. If it takes longer to boot, the action would fail - e.g. `300` for 5 minutes'
|
||||
default: '600'
|
||||
emulator-port:
|
||||
description: 'Port to run emulator on, allows to run multiple emulators on the same physical machine'
|
||||
default: '5554'
|
||||
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'
|
||||
disable-animations:
|
||||
description: 'whether to disable animations - true or false'
|
||||
default: 'true'
|
||||
disable-spellchecker:
|
||||
description: 'whether to disable the Android spell checker framework, a common source of flakiness in text fields - `true` or `false`'
|
||||
default: 'false'
|
||||
disable-linux-hw-accel:
|
||||
description: 'whether to disable hardware acceleration on Linux machines - `true` or `false` or `auto`'
|
||||
default: 'auto'
|
||||
enable-hw-keyboard:
|
||||
description: 'whether to enable hardware keyboard - `true` or `false`.'
|
||||
default: 'false'
|
||||
emulator-build:
|
||||
description: 'build number of a specific version of the emulator binary to use - e.g. `6061023` for emulator v29.3.0.0'
|
||||
working-directory:
|
||||
@@ -33,9 +65,14 @@ inputs:
|
||||
description: 'version of NDK to install - e.g. 21.0.6113669'
|
||||
cmake:
|
||||
description: 'version of CMake to install - e.g. 3.10.2.4988404'
|
||||
channel:
|
||||
description: 'Channel to download the SDK components from - `stable`, `beta`, `dev`, `canary`'
|
||||
default: 'stable'
|
||||
script:
|
||||
description: 'custom script to run - e.g. `./gradlew connectedCheck`'
|
||||
required: true
|
||||
pre-emulator-launch-script:
|
||||
description: 'custom script to run after creating the AVD and before launching the emulator - e.g. `./adjust-emulator-configs.sh`'
|
||||
runs:
|
||||
using: 'node12'
|
||||
using: 'node24'
|
||||
main: 'lib/main.js'
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getChannelId = getChannelId;
|
||||
function getChannelId(channelName) {
|
||||
if (channelName === 'stable') {
|
||||
return 0;
|
||||
}
|
||||
else if (channelName === 'beta') {
|
||||
return 1;
|
||||
}
|
||||
else if (channelName === 'dev') {
|
||||
return 2;
|
||||
}
|
||||
else if (channelName === 'canary') {
|
||||
return 3;
|
||||
}
|
||||
else {
|
||||
throw new Error(`Unexpected channel name: '${channelName}'.`);
|
||||
}
|
||||
}
|
||||
+138
-88
@@ -1,7 +1,11 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
@@ -11,120 +15,166 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
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());
|
||||
});
|
||||
};
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.killEmulator = exports.launchEmulator = void 0;
|
||||
exports.createAvd = createAvd;
|
||||
exports.launchEmulator = launchEmulator;
|
||||
exports.killEmulator = killEmulator;
|
||||
const exec = __importStar(require("@actions/exec"));
|
||||
const EMULATOR_BOOT_TIMEOUT_SECONDS = 600;
|
||||
const fs = __importStar(require("fs"));
|
||||
/**
|
||||
* Creates and launches a new AVD instance with the specified configurations.
|
||||
* Creates a new AVD instance with the specified configurations.
|
||||
*/
|
||||
function launchEmulator(apiLevel, target, arch, profile, avdName, emulatorOptions, disableAnimations) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
// create a new AVD
|
||||
if (profile.trim() !== '') {
|
||||
console.log(`Creating AVD with custom profile ${profile}`);
|
||||
yield exec.exec(`avdmanager create avd --force -n "${avdName}" --abi "${target}/${arch}" --package "system-images;android-${apiLevel};${target};${arch}" --device "${profile}"`);
|
||||
async function createAvd(arch, avdName, cores, diskSize, enableHardwareKeyboard, forceAvdCreation, heapSize, profile, ramSize, sdcardPathOrSize, systemImageApiLevel, target) {
|
||||
try {
|
||||
console.log(`::group::Create AVD`);
|
||||
// create a new AVD if AVD directory does not already exist or forceAvdCreation is true
|
||||
const avdPath = `${process.env.ANDROID_AVD_HOME}/${avdName}.avd`;
|
||||
if (!fs.existsSync(avdPath) || forceAvdCreation) {
|
||||
const profileOption = profile.trim() !== '' ? `--device '${profile}'` : '';
|
||||
const sdcardPathOrSizeOption = sdcardPathOrSize.trim() !== '' ? `--sdcard '${sdcardPathOrSize}'` : '';
|
||||
console.log(`Creating AVD.`);
|
||||
await exec.exec(`sh -c \\"echo no | avdmanager create avd --force -n "${avdName}" --package 'system-images;android-${systemImageApiLevel};${target};${arch}' ${profileOption} ${sdcardPathOrSizeOption}"`);
|
||||
}
|
||||
else {
|
||||
console.log(`Creating AVD without custom profile.`);
|
||||
yield exec.exec(`sh -c \\"echo no | avdmanager create avd --force -n "${avdName}" --abi '${target}/${arch}' --package 'system-images;android-${apiLevel};${target};${arch}'"`);
|
||||
if (cores || ramSize || heapSize || enableHardwareKeyboard || diskSize) {
|
||||
const configEntries = [];
|
||||
if (cores) {
|
||||
configEntries.push(`hw.cpu.ncore=${cores}`);
|
||||
}
|
||||
if (ramSize) {
|
||||
configEntries.push(`hw.ramSize=${ramSize}`);
|
||||
}
|
||||
if (heapSize) {
|
||||
configEntries.push(`hw.heapSize=${heapSize}`);
|
||||
}
|
||||
if (enableHardwareKeyboard) {
|
||||
configEntries.push('hw.keyboard=yes');
|
||||
}
|
||||
if (diskSize) {
|
||||
configEntries.push(`disk.dataPartition.size=${diskSize}`);
|
||||
}
|
||||
if (configEntries.length > 0) {
|
||||
const configContent = configEntries.join('\\n') + '\\n';
|
||||
await exec.exec(`sh -c \\"printf '${configContent}' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini"`);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
console.log(`::endgroup::`);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Launches an existing AVD instance with the specified configurations.
|
||||
*/
|
||||
async function launchEmulator(avdName, disableAnimations, disableLinuxHardwareAcceleration, disableSpellChecker, emulatorBootTimeout, emulatorOptions, enableHardwareKeyboard, port) {
|
||||
try {
|
||||
console.log(`::group::Launch Emulator`);
|
||||
// turn off hardware acceleration on Linux
|
||||
if (process.platform === 'linux' && disableLinuxHardwareAcceleration) {
|
||||
console.log('Disabling Linux hardware acceleration.');
|
||||
emulatorOptions += ' -accel off';
|
||||
}
|
||||
// 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 "${avdName}" ${emulatorOptions} &"`, [], {
|
||||
await exec.exec(`sh -c \\"${process.env.ANDROID_HOME}/emulator/emulator -port ${port} -avd "${avdName}" ${emulatorOptions} &"`, [], {
|
||||
listeners: {
|
||||
stderr: (data) => {
|
||||
if (data.toString().includes('invalid command-line parameter')) {
|
||||
throw new Error(data.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
// wait for emulator to complete booting
|
||||
yield waitForDevice();
|
||||
yield exec.exec(`adb shell input keyevent 82`);
|
||||
// disable animations
|
||||
await waitForDevice(port, emulatorBootTimeout);
|
||||
await adb(port, `shell input keyevent 82`);
|
||||
if (disableAnimations) {
|
||||
console.log('Disabling animations.');
|
||||
yield exec.exec(`adb shell settings put global window_animation_scale 0.0`);
|
||||
yield exec.exec(`adb shell settings put global transition_animation_scale 0.0`);
|
||||
yield exec.exec(`adb shell settings put global animator_duration_scale 0.0`);
|
||||
await adb(port, `shell settings put global window_animation_scale 0.0`);
|
||||
await adb(port, `shell settings put global transition_animation_scale 0.0`);
|
||||
await adb(port, `shell settings put global animator_duration_scale 0.0`);
|
||||
}
|
||||
});
|
||||
if (disableSpellChecker) {
|
||||
await adb(port, `shell settings put secure spell_checker_enabled 0`);
|
||||
}
|
||||
if (enableHardwareKeyboard) {
|
||||
await adb(port, `shell settings put secure show_ime_with_hard_keyboard 0`);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
console.log(`::endgroup::`);
|
||||
}
|
||||
}
|
||||
exports.launchEmulator = launchEmulator;
|
||||
/**
|
||||
* Kills the running emulator on the defaut port.
|
||||
* Kills the running emulator on the default port.
|
||||
*/
|
||||
function killEmulator() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
try {
|
||||
yield exec.exec(`adb -s emulator-5554 emu kill`);
|
||||
}
|
||||
catch (error) {
|
||||
console.log(error.message);
|
||||
}
|
||||
});
|
||||
async function killEmulator(port) {
|
||||
try {
|
||||
console.log(`::group::Terminate Emulator`);
|
||||
await adb(port, `emu kill`);
|
||||
}
|
||||
catch (error) {
|
||||
console.log(error instanceof Error ? error.message : error);
|
||||
}
|
||||
finally {
|
||||
console.log(`::endgroup::`);
|
||||
}
|
||||
}
|
||||
async function adb(port, command) {
|
||||
return await exec.exec(`adb -s emulator-${port} ${command}`);
|
||||
}
|
||||
exports.killEmulator = killEmulator;
|
||||
/**
|
||||
* Wait for emulator to boot.
|
||||
*/
|
||||
function waitForDevice() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
let booted = false;
|
||||
let attempts = 0;
|
||||
const retryInterval = 2; // retry every 2 seconds
|
||||
const maxAttemps = EMULATOR_BOOT_TIMEOUT_SECONDS / 2;
|
||||
while (!booted) {
|
||||
try {
|
||||
let result = '';
|
||||
yield exec.exec(`adb shell getprop sys.boot_completed`, [], {
|
||||
listeners: {
|
||||
stdout: (data) => {
|
||||
result += data.toString();
|
||||
}
|
||||
}
|
||||
});
|
||||
if (result.trim() === '1') {
|
||||
console.log('Emulator booted.');
|
||||
booted = true;
|
||||
break;
|
||||
}
|
||||
async function waitForDevice(port, emulatorBootTimeout) {
|
||||
let booted = false;
|
||||
let attempts = 0;
|
||||
const retryInterval = 2; // retry every 2 seconds
|
||||
const maxAttempts = emulatorBootTimeout / 2;
|
||||
while (!booted) {
|
||||
try {
|
||||
let result = '';
|
||||
await exec.exec(`adb -s emulator-${port} shell getprop sys.boot_completed`, [], {
|
||||
listeners: {
|
||||
stdout: (data) => {
|
||||
result += data.toString();
|
||||
},
|
||||
},
|
||||
});
|
||||
if (result.trim() === '1') {
|
||||
console.log('Emulator booted.');
|
||||
booted = true;
|
||||
break;
|
||||
}
|
||||
catch (error) {
|
||||
console.warn(error.message);
|
||||
}
|
||||
if (attempts < maxAttemps) {
|
||||
yield delay(retryInterval * 1000);
|
||||
}
|
||||
else {
|
||||
throw new Error(`Timeout waiting for emulator to boot.`);
|
||||
}
|
||||
attempts++;
|
||||
}
|
||||
});
|
||||
catch (error) {
|
||||
console.warn(error instanceof Error ? error.message : error);
|
||||
}
|
||||
if (attempts < maxAttempts) {
|
||||
await delay(retryInterval * 1000);
|
||||
}
|
||||
else {
|
||||
throw new Error(`Timeout waiting for emulator to boot.`);
|
||||
}
|
||||
attempts++;
|
||||
}
|
||||
}
|
||||
function delay(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
+77
-21
@@ -1,39 +1,95 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.checkEmulatorBuild = exports.checkDisableAnimations = exports.checkArch = exports.checkTarget = exports.checkApiLevel = exports.VALID_ARCHS = exports.VALID_TARGETS = exports.MIN_API_LEVEL = void 0;
|
||||
exports.MAX_PORT = exports.MIN_PORT = exports.VALID_CHANNELS = exports.VALID_ARCHS = exports.MIN_API_LEVEL = void 0;
|
||||
exports.playstoreTargetSubstitution = playstoreTargetSubstitution;
|
||||
exports.checkArch = checkArch;
|
||||
exports.checkChannel = checkChannel;
|
||||
exports.checkForceAvdCreation = checkForceAvdCreation;
|
||||
exports.checkPort = checkPort;
|
||||
exports.checkDisableAnimations = checkDisableAnimations;
|
||||
exports.checkDisableSpellchecker = checkDisableSpellchecker;
|
||||
exports.checkDisableLinuxHardwareAcceleration = checkDisableLinuxHardwareAcceleration;
|
||||
exports.checkEnableHardwareKeyboard = checkEnableHardwareKeyboard;
|
||||
exports.checkEmulatorBuild = checkEmulatorBuild;
|
||||
exports.checkDiskSize = checkDiskSize;
|
||||
exports.MIN_API_LEVEL = 15;
|
||||
exports.VALID_TARGETS = ['default', 'google_apis', 'google_apis_playstore'];
|
||||
exports.VALID_ARCHS = ['x86', 'x86_64'];
|
||||
function checkApiLevel(apiLevel) {
|
||||
if (isNaN(Number(apiLevel)) || !Number.isInteger(Number(apiLevel))) {
|
||||
throw new Error(`Unexpected API level: '${apiLevel}'.`);
|
||||
}
|
||||
if (Number(apiLevel) < exports.MIN_API_LEVEL) {
|
||||
throw new Error(`Minimum API level supported is ${exports.MIN_API_LEVEL}.`);
|
||||
}
|
||||
exports.VALID_ARCHS = ['x86', 'x86_64', 'arm64-v8a'];
|
||||
exports.VALID_CHANNELS = ['stable', 'beta', 'dev', 'canary'];
|
||||
exports.MIN_PORT = 5554;
|
||||
exports.MAX_PORT = 5584;
|
||||
function playstoreTargetSubstitution(target) {
|
||||
// "playstore" is an allowed shorthand for "google_apis_playstore" images
|
||||
// this is idempotent - return same even if run multiple times on same target
|
||||
if (target === 'playstore')
|
||||
return 'google_apis_playstore';
|
||||
if (target === 'playstore_ps16k')
|
||||
return 'google_apis_playstore_ps16k';
|
||||
return target;
|
||||
}
|
||||
exports.checkApiLevel = checkApiLevel;
|
||||
function checkTarget(target) {
|
||||
if (!exports.VALID_TARGETS.includes(target)) {
|
||||
throw new Error(`Value for input.target '${target}' is unknown. Supported options: ${exports.VALID_TARGETS}.`);
|
||||
}
|
||||
}
|
||||
exports.checkTarget = checkTarget;
|
||||
function checkArch(arch) {
|
||||
if (!exports.VALID_ARCHS.includes(arch)) {
|
||||
throw new Error(`Value for input.arch '${arch}' is unknown. Supported options: ${exports.VALID_ARCHS}.`);
|
||||
}
|
||||
}
|
||||
exports.checkArch = checkArch;
|
||||
function checkChannel(channel) {
|
||||
if (!exports.VALID_CHANNELS.includes(channel)) {
|
||||
throw new Error(`Value for input.channel '${channel}' is unknown. Supported options: ${exports.VALID_CHANNELS}.`);
|
||||
}
|
||||
}
|
||||
function checkForceAvdCreation(forceAvdCreation) {
|
||||
if (!isValidBoolean(forceAvdCreation)) {
|
||||
throw new Error(`Input for input.force-avd-creation should be either 'true' or 'false'.`);
|
||||
}
|
||||
}
|
||||
function checkPort(port) {
|
||||
if (port < exports.MIN_PORT || port > exports.MAX_PORT) {
|
||||
throw new Error(`Emulator port is outside of the supported port range [${exports.MIN_PORT}, ${exports.MAX_PORT}], was ${port}`);
|
||||
}
|
||||
if (port % 2 == 1) {
|
||||
throw new Error(`Emulator port has to be even, was ${port}`);
|
||||
}
|
||||
}
|
||||
function checkDisableAnimations(disableAnimations) {
|
||||
if (disableAnimations !== 'true' && disableAnimations !== 'false') {
|
||||
if (!isValidBoolean(disableAnimations)) {
|
||||
throw new Error(`Input for input.disable-animations should be either 'true' or 'false'.`);
|
||||
}
|
||||
}
|
||||
exports.checkDisableAnimations = checkDisableAnimations;
|
||||
function checkDisableSpellchecker(disableSpellchecker) {
|
||||
if (!isValidBoolean(disableSpellchecker)) {
|
||||
throw new Error(`Input for input.disable-spellchecker should be either 'true' or 'false'.`);
|
||||
}
|
||||
}
|
||||
function checkDisableLinuxHardwareAcceleration(disableLinuxHardwareAcceleration) {
|
||||
if (!(isValidBoolean(disableLinuxHardwareAcceleration) || disableLinuxHardwareAcceleration === 'auto')) {
|
||||
throw new Error(`Input for input.disable-linux-hw-accel should be either 'true' or 'false' or 'auto'.`);
|
||||
}
|
||||
}
|
||||
function checkEnableHardwareKeyboard(enableHardwareKeyboard) {
|
||||
if (!isValidBoolean(enableHardwareKeyboard)) {
|
||||
throw new Error(`Input for input.enable-hw-keyboard should be either 'true' or 'false'.`);
|
||||
}
|
||||
}
|
||||
function checkEmulatorBuild(emulatorBuild) {
|
||||
if (isNaN(Number(emulatorBuild)) || !Number.isInteger(Number(emulatorBuild))) {
|
||||
throw new Error(`Unexpected emulator build: '${emulatorBuild}'.`);
|
||||
}
|
||||
}
|
||||
exports.checkEmulatorBuild = checkEmulatorBuild;
|
||||
function isValidBoolean(value) {
|
||||
return value === 'true' || value === 'false';
|
||||
}
|
||||
function checkDiskSize(diskSize) {
|
||||
// Disk size can be empty - the default value
|
||||
if (diskSize) {
|
||||
// Can also be number of bytes
|
||||
if (isNaN(Number(diskSize)) || !Number.isInteger(Number(diskSize))) {
|
||||
// Disk size can have a size multiplier at the end K, M or G
|
||||
const diskSizeUpperCase = diskSize.toUpperCase();
|
||||
if (diskSizeUpperCase.endsWith('K') || diskSizeUpperCase.endsWith('M') || diskSizeUpperCase.endsWith('G')) {
|
||||
const diskSizeNoModifier = diskSize.slice(0, -1);
|
||||
if (0 == diskSizeNoModifier.length || isNaN(Number(diskSizeNoModifier)) || !Number.isInteger(Number(diskSizeNoModifier))) {
|
||||
throw new Error(`Unexpected disk size: '${diskSize}'.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+199
-103
@@ -1,7 +1,11 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
@@ -11,22 +15,23 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
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());
|
||||
});
|
||||
};
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const core = __importStar(require("@actions/core"));
|
||||
const sdk_installer_1 = require("./sdk-installer");
|
||||
@@ -34,103 +39,194 @@ 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");
|
||||
function run() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
try {
|
||||
// only support running on macOS or Linux
|
||||
if (process.platform !== 'darwin') {
|
||||
if (process.platform === 'linux') {
|
||||
const channel_id_mapper_1 = require("./channel-id-mapper");
|
||||
const fs_1 = require("fs");
|
||||
async function run() {
|
||||
let port = input_validator_1.MIN_PORT;
|
||||
try {
|
||||
console.log(`::group::Configure emulator`);
|
||||
let linuxSupportKVM = false;
|
||||
// only support running on macOS or Linux
|
||||
if (process.platform !== 'darwin') {
|
||||
if (process.platform === 'linux') {
|
||||
try {
|
||||
(0, fs_1.accessSync)('/dev/kvm', fs_1.constants.R_OK | fs_1.constants.W_OK);
|
||||
linuxSupportKVM = true;
|
||||
}
|
||||
catch {
|
||||
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 });
|
||||
input_validator_1.checkApiLevel(apiLevelInput);
|
||||
const apiLevel = Number(apiLevelInput);
|
||||
console.log(`API level: ${apiLevel}`);
|
||||
// target of the system image
|
||||
const targetInput = core.getInput('target');
|
||||
const target = targetInput == 'playstore' ? 'google_apis_playstore' : targetInput;
|
||||
input_validator_1.checkTarget(target);
|
||||
console.log(`target: ${target}`);
|
||||
// CPU architecture of the system image
|
||||
const arch = core.getInput('arch');
|
||||
input_validator_1.checkArch(arch);
|
||||
console.log(`CPU architecture: ${arch}`);
|
||||
// Hardware profile used for creating the AVD
|
||||
const profile = core.getInput('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
|
||||
const emulatorOptions = core.getInput('emulator-options').trim();
|
||||
console.log(`emulator options: ${emulatorOptions}`);
|
||||
// disable animations
|
||||
const disableAnimationsInput = core.getInput('disable-animations');
|
||||
input_validator_1.checkDisableAnimations(disableAnimationsInput);
|
||||
const disableAnimations = disableAnimationsInput === 'true';
|
||||
console.log(`disable animations: ${disableAnimations}`);
|
||||
// emulator build
|
||||
const emulatorBuildInput = core.getInput('emulator-build');
|
||||
if (emulatorBuildInput) {
|
||||
input_validator_1.checkEmulatorBuild(emulatorBuildInput);
|
||||
console.log(`using emulator build: ${emulatorBuildInput}`);
|
||||
else {
|
||||
throw new Error('Unsupported virtual machine: please use either macos or ubuntu VM.');
|
||||
}
|
||||
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);
|
||||
console.log(`Script:`);
|
||||
scripts.forEach((script) => __awaiter(this, void 0, void 0, function* () {
|
||||
console.log(`${script}`);
|
||||
}));
|
||||
// install SDK
|
||||
yield sdk_installer_1.installAndroidSdk(apiLevel, target, arch, emulatorBuild, ndkVersion, cmakeVersion);
|
||||
// launch an emulator
|
||||
yield emulator_manager_1.launchEmulator(apiLevel, target, arch, profile, avdName, emulatorOptions, disableAnimations);
|
||||
// execute the custom script
|
||||
}
|
||||
// API level of the platform and system image
|
||||
const apiLevel = core.getInput('api-level', { required: true });
|
||||
console.log(`API level: ${apiLevel}`);
|
||||
let systemImageApiLevel = core.getInput('system-image-api-level');
|
||||
if (!systemImageApiLevel) {
|
||||
systemImageApiLevel = apiLevel;
|
||||
}
|
||||
console.log(`System image API level: ${systemImageApiLevel}`);
|
||||
// target of the system image
|
||||
const target = (0, input_validator_1.playstoreTargetSubstitution)(core.getInput('target'));
|
||||
console.log(`target: ${target}`);
|
||||
// CPU architecture of the system image
|
||||
const arch = core.getInput('arch');
|
||||
(0, input_validator_1.checkArch)(arch);
|
||||
console.log(`CPU architecture: ${arch}`);
|
||||
// Hardware profile used for creating the AVD
|
||||
const profile = core.getInput('profile');
|
||||
console.log(`Hardware profile: ${profile}`);
|
||||
// Number of cores to use for emulator
|
||||
const cores = core.getInput('cores');
|
||||
console.log(`Cores: ${cores}`);
|
||||
// RAM to use for AVD
|
||||
const ramSize = core.getInput('ram-size');
|
||||
console.log(`RAM size: ${ramSize}`);
|
||||
// Heap size to use for AVD
|
||||
const heapSize = core.getInput('heap-size');
|
||||
console.log(`Heap size: ${heapSize}`);
|
||||
// SD card path or size used for creating the AVD
|
||||
const sdcardPathOrSize = core.getInput('sdcard-path-or-size');
|
||||
console.log(`SD card path or size: ${sdcardPathOrSize}`);
|
||||
const diskSize = core.getInput('disk-size');
|
||||
(0, input_validator_1.checkDiskSize)(diskSize);
|
||||
console.log(`Disk size: ${diskSize}`);
|
||||
// custom name used for creating the AVD
|
||||
const avdName = core.getInput('avd-name');
|
||||
console.log(`AVD name: ${avdName}`);
|
||||
// force AVD creation
|
||||
const forceAvdCreationInput = core.getInput('force-avd-creation');
|
||||
(0, input_validator_1.checkForceAvdCreation)(forceAvdCreationInput);
|
||||
const forceAvdCreation = forceAvdCreationInput === 'true';
|
||||
console.log(`force avd creation: ${forceAvdCreation}`);
|
||||
// Emulator boot timeout seconds
|
||||
const emulatorBootTimeout = parseInt(core.getInput('emulator-boot-timeout'), 10);
|
||||
console.log(`Emulator boot timeout: ${emulatorBootTimeout}`);
|
||||
// Emulator port to use
|
||||
port = parseInt(core.getInput('emulator-port'), 10);
|
||||
(0, input_validator_1.checkPort)(port);
|
||||
console.log(`emulator port: ${port}`);
|
||||
// emulator options
|
||||
const emulatorOptions = core.getInput('emulator-options').trim();
|
||||
console.log(`emulator options: ${emulatorOptions}`);
|
||||
// disable animations
|
||||
const disableAnimationsInput = core.getInput('disable-animations');
|
||||
(0, input_validator_1.checkDisableAnimations)(disableAnimationsInput);
|
||||
const disableAnimations = disableAnimationsInput === 'true';
|
||||
console.log(`disable animations: ${disableAnimations}`);
|
||||
// disable spellchecker
|
||||
const disableSpellcheckerInput = core.getInput('disable-spellchecker');
|
||||
(0, input_validator_1.checkDisableSpellchecker)(disableSpellcheckerInput);
|
||||
const disableSpellchecker = disableSpellcheckerInput === 'true';
|
||||
console.log(`disable spellchecker: ${disableSpellchecker}`);
|
||||
// disable linux hardware acceleration
|
||||
let disableLinuxHardwareAccelerationInput = core.getInput('disable-linux-hw-accel');
|
||||
(0, input_validator_1.checkDisableLinuxHardwareAcceleration)(disableLinuxHardwareAccelerationInput);
|
||||
if (disableLinuxHardwareAccelerationInput === 'auto' && process.platform === 'linux') {
|
||||
disableLinuxHardwareAccelerationInput = linuxSupportKVM ? 'false' : 'true';
|
||||
}
|
||||
const disableLinuxHardwareAcceleration = disableLinuxHardwareAccelerationInput === 'true';
|
||||
console.log(`disable Linux hardware acceleration: ${disableLinuxHardwareAcceleration}`);
|
||||
// enable hardware keyboard
|
||||
const enableHardwareKeyboardInput = core.getInput('enable-hw-keyboard');
|
||||
(0, input_validator_1.checkEnableHardwareKeyboard)(enableHardwareKeyboardInput);
|
||||
const enableHardwareKeyboard = enableHardwareKeyboardInput === 'true';
|
||||
console.log(`enable hardware keyboard: ${enableHardwareKeyboard}`);
|
||||
// emulator build
|
||||
const emulatorBuildInput = core.getInput('emulator-build');
|
||||
if (emulatorBuildInput) {
|
||||
(0, input_validator_1.checkEmulatorBuild)(emulatorBuildInput);
|
||||
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;
|
||||
// channelId (up to and including) to download the SDK packages from
|
||||
const channelName = core.getInput('channel');
|
||||
(0, input_validator_1.checkChannel)(channelName);
|
||||
const channelId = (0, channel_id_mapper_1.getChannelId)(channelName);
|
||||
console.log(`Channel: ${channelId} (${channelName})`);
|
||||
// custom script to run
|
||||
const scriptInput = core.getInput('script', { required: true });
|
||||
const scripts = (0, script_parser_1.parseScript)(scriptInput);
|
||||
console.log(`Script:`);
|
||||
scripts.forEach(async (script) => {
|
||||
console.log(`${script}`);
|
||||
});
|
||||
// custom pre emulator launch script
|
||||
const preEmulatorLaunchScriptInput = core.getInput('pre-emulator-launch-script');
|
||||
const preEmulatorLaunchScripts = !preEmulatorLaunchScriptInput ? undefined : (0, script_parser_1.parseScript)(preEmulatorLaunchScriptInput);
|
||||
console.log(`Pre emulator launch script:`);
|
||||
preEmulatorLaunchScripts?.forEach(async (script) => {
|
||||
console.log(`${script}`);
|
||||
});
|
||||
console.log(`::endgroup::`);
|
||||
// install SDK
|
||||
await (0, sdk_installer_1.installAndroidSdk)(apiLevel, systemImageApiLevel, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion);
|
||||
// create AVD
|
||||
await (0, emulator_manager_1.createAvd)(arch, avdName, cores, diskSize, enableHardwareKeyboard, forceAvdCreation, heapSize, profile, ramSize, sdcardPathOrSize, systemImageApiLevel, target);
|
||||
// execute pre emulator launch script if set
|
||||
if (preEmulatorLaunchScripts !== undefined) {
|
||||
console.log(`::group::Run pre emulator launch script`);
|
||||
try {
|
||||
// move to custom working directory if set
|
||||
if (workingDirectory) {
|
||||
process.chdir(workingDirectory);
|
||||
}
|
||||
for (const script of scripts) {
|
||||
yield exec.exec(`sh -c \\"${script}"`);
|
||||
for (const preEmulatorLaunchScript of preEmulatorLaunchScripts) {
|
||||
// use array form to avoid various quote escaping problems
|
||||
// caused by exec(`sh -c "${preEmulatorLaunchScript}"`)
|
||||
await exec.exec('sh', ['-c', preEmulatorLaunchScript], {
|
||||
cwd: workingDirectory,
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
core.setFailed(error.message);
|
||||
core.setFailed(error instanceof Error ? error.message : error);
|
||||
}
|
||||
console.log(`::endgroup::`);
|
||||
}
|
||||
// launch an emulator
|
||||
await (0, emulator_manager_1.launchEmulator)(avdName, disableAnimations, disableLinuxHardwareAcceleration, disableSpellchecker, emulatorBootTimeout, emulatorOptions, enableHardwareKeyboard, port);
|
||||
// execute the custom script
|
||||
try {
|
||||
// move to custom working directory if set
|
||||
if (workingDirectory) {
|
||||
process.chdir(workingDirectory);
|
||||
}
|
||||
for (const script of scripts) {
|
||||
// use array form to avoid various quote escaping problems
|
||||
// caused by exec(`sh -c "${script}"`)
|
||||
await exec.exec('sh', ['-c', script], {
|
||||
env: { ...process.env, EMULATOR_PORT: `${port}`, ANDROID_SERIAL: `emulator-${port}` },
|
||||
});
|
||||
}
|
||||
// finally kill the emulator
|
||||
yield emulator_manager_1.killEmulator();
|
||||
}
|
||||
catch (error) {
|
||||
// kill the emulator so the action can exit
|
||||
yield emulator_manager_1.killEmulator();
|
||||
core.setFailed(error.message);
|
||||
core.setFailed(error instanceof Error ? error.message : error);
|
||||
}
|
||||
});
|
||||
// finally kill the emulator
|
||||
await (0, emulator_manager_1.killEmulator)(port);
|
||||
}
|
||||
catch (error) {
|
||||
// kill the emulator so the action can exit
|
||||
await (0, emulator_manager_1.killEmulator)(port);
|
||||
core.setFailed(error instanceof Error ? error.message : error);
|
||||
}
|
||||
}
|
||||
run();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.parseScript = void 0;
|
||||
exports.parseScript = parseScript;
|
||||
/**
|
||||
* Convert a (potentially multi-line) script to an array of single-line script(s).
|
||||
*/
|
||||
@@ -17,4 +17,3 @@ function parseScript(rawScript) {
|
||||
}
|
||||
return scripts;
|
||||
}
|
||||
exports.parseScript = parseScript;
|
||||
|
||||
+73
-56
@@ -1,7 +1,11 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
@@ -11,85 +15,98 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
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());
|
||||
});
|
||||
};
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.installAndroidSdk = void 0;
|
||||
exports.installAndroidSdk = installAndroidSdk;
|
||||
const core = __importStar(require("@actions/core"));
|
||||
const exec = __importStar(require("@actions/exec"));
|
||||
const io = __importStar(require("@actions/io"));
|
||||
const tc = __importStar(require("@actions/tool-cache"));
|
||||
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-6609375_latest.zip';
|
||||
const CMDLINE_TOOLS_URL_LINUX = 'https://dl.google.com/android/repository/commandlinetools-linux-6609375_latest.zip';
|
||||
const BUILD_TOOLS_VERSION = '37.0.0';
|
||||
// SDK command-line tools 23.0
|
||||
const CMDLINE_TOOLS_VERSION = '16111833';
|
||||
const CMDLINE_TOOLS_URL_MAC = `https://dl.google.com/android/repository/commandlinetools-mac_x86_64-${CMDLINE_TOOLS_VERSION}_latest.zip`;
|
||||
const CMDLINE_TOOLS_URL_LINUX = `https://dl.google.com/android/repository/commandlinetools-linux-${CMDLINE_TOOLS_VERSION}_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, ndkVersion, cmakeVersion) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
async function installAndroidSdk(apiLevel, systemImageApiLevel, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion) {
|
||||
try {
|
||||
console.log(`::group::Install Android SDK`);
|
||||
const isOnMac = process.platform === 'darwin';
|
||||
if (!isOnMac) {
|
||||
yield exec.exec(`sh -c \\"sudo chown $USER:$USER ${process.env.ANDROID_HOME} -R`);
|
||||
}
|
||||
const isArm = process.arch === 'arm64';
|
||||
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;
|
||||
yield io.mkdirP(`${process.env.ANDROID_HOME}/cmdline-tools`);
|
||||
yield exec.exec(`curl -fo commandlinetools.zip ${sdkUrl}`);
|
||||
yield exec.exec(`unzip -q commandlinetools.zip -d ${cmdlineToolsPath}`);
|
||||
yield io.rmRF('commandlinetools.zip');
|
||||
// add paths for commandline-tools and platform-tools
|
||||
core.addPath(`${cmdlineToolsPath}/tools:${cmdlineToolsPath}/tools/bin:${process.env.ANDROID_HOME}/platform-tools`);
|
||||
}
|
||||
// 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');
|
||||
const downloadPath = await tc.downloadTool(sdkUrl);
|
||||
await tc.extractZip(downloadPath, cmdlineToolsPath);
|
||||
await io.mv(`${cmdlineToolsPath}/cmdline-tools`, `${cmdlineToolsPath}/latest`);
|
||||
}
|
||||
// add paths for commandline-tools and platform-tools
|
||||
core.addPath(`${cmdlineToolsPath}/latest:${cmdlineToolsPath}/latest/bin:${process.env.ANDROID_HOME}/platform-tools`);
|
||||
// set standard AVD path
|
||||
await io.mkdirP(`${process.env.HOME}/.android/avd`);
|
||||
core.exportVariable('ANDROID_AVD_HOME', `${process.env.HOME}/.android/avd`);
|
||||
// accept all Android SDK licenses
|
||||
await exec.exec(`sh -c \\"yes | sdkmanager --licenses > /dev/null"`);
|
||||
console.log('Installing latest build tools, platform tools, and platform.');
|
||||
yield exec.exec(`sh -c \\"sdkmanager --install 'build-tools;${BUILD_TOOLS_VERSION}' platform-tools 'platforms;android-${apiLevel}' > /dev/null"`);
|
||||
await exec.exec(`sh -c \\"sdkmanager --install 'build-tools;${BUILD_TOOLS_VERSION}' platform-tools 'platforms;android-${apiLevel}'> /dev/null"`);
|
||||
console.log('Installing latest emulator.');
|
||||
await exec.exec(`sh -c \\"sdkmanager --install emulator --channel=${channelId} > /dev/null"`);
|
||||
if (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 io.rmRF(`${process.env.ANDROID_HOME}/emulator`);
|
||||
yield exec.exec(`unzip -q emulator.zip -d ${process.env.ANDROID_HOME}`);
|
||||
yield io.rmRF('emulator.zip');
|
||||
}
|
||||
else {
|
||||
console.log('Installing latest emulator.');
|
||||
yield exec.exec(`sh -c \\"sdkmanager --install emulator > /dev/null"`);
|
||||
// TODO find out the correct download URLs for all build ids
|
||||
var downloadUrlSuffix;
|
||||
const majorBuildVersion = Number(emulatorBuild);
|
||||
if (majorBuildVersion >= 8000000) {
|
||||
if (isArm) {
|
||||
downloadUrlSuffix = `_aarch64-${emulatorBuild}`;
|
||||
}
|
||||
else {
|
||||
downloadUrlSuffix = `_x64-${emulatorBuild}`;
|
||||
}
|
||||
}
|
||||
else if (majorBuildVersion >= 7000000) {
|
||||
downloadUrlSuffix = `_x64-${emulatorBuild}`;
|
||||
}
|
||||
else {
|
||||
downloadUrlSuffix = `-${emulatorBuild}`;
|
||||
}
|
||||
await exec.exec(`curl -fo emulator.zip https://dl.google.com/android/repository/emulator-${isOnMac ? 'darwin' : 'linux'}${downloadUrlSuffix}.zip`);
|
||||
await exec.exec(`unzip -o -q emulator.zip -d ${process.env.ANDROID_HOME}`);
|
||||
await io.rmRF('emulator.zip');
|
||||
}
|
||||
console.log('Installing system images.');
|
||||
yield exec.exec(`sh -c \\"sdkmanager --install 'system-images;android-${apiLevel};${target};${arch}' > /dev/null"`);
|
||||
await exec.exec(`sh -c \\"sdkmanager --install 'system-images;android-${systemImageApiLevel};${target};${arch}' --channel=${channelId} > /dev/null"`);
|
||||
if (ndkVersion) {
|
||||
console.log(`Installing NDK ${ndkVersion}.`);
|
||||
yield exec.exec(`sh -c \\"sdkmanager --install 'ndk;${ndkVersion}' > /dev/null"`);
|
||||
await exec.exec(`sh -c \\"sdkmanager --install 'ndk;${ndkVersion}' --channel=${channelId} > /dev/null"`);
|
||||
}
|
||||
if (cmakeVersion) {
|
||||
console.log(`Installing CMake ${cmakeVersion}.`);
|
||||
yield exec.exec(`sh -c \\"sdkmanager --install 'cmake;${cmakeVersion}' > /dev/null"`);
|
||||
await exec.exec(`sh -c \\"sdkmanager --install 'cmake;${cmakeVersion}' --channel=${channelId} > /dev/null"`);
|
||||
}
|
||||
});
|
||||
}
|
||||
finally {
|
||||
console.log(`::endgroup::`);
|
||||
}
|
||||
}
|
||||
exports.installAndroidSdk = installAndroidSdk;
|
||||
|
||||
Generated
+9778
-6964
File diff suppressed because it is too large
Load Diff
+22
-18
@@ -2,7 +2,7 @@
|
||||
"name": "android-emulator-runner",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"description": "A GitHub Action for installing, configuring and running Android Emulators on macOS virtual machines.",
|
||||
"description": "A GitHub Action for installing, configuring and running Android Emulators on hardware-accelerated runners.",
|
||||
"main": "lib/main.js",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
@@ -18,27 +18,31 @@
|
||||
"node",
|
||||
"setup"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=24"
|
||||
},
|
||||
"author": "Reactive Circus",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.2.6",
|
||||
"@actions/exec": "^1.0.4",
|
||||
"@actions/tool-cache": "^1.6.0",
|
||||
"minimist": "^1.2.5"
|
||||
"@actions/core": "^1.10.0",
|
||||
"@actions/exec": "^1.1.1",
|
||||
"@actions/tool-cache": "^2.0.1",
|
||||
"minimist": "^1.2.7",
|
||||
"set-value": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^24.9.1",
|
||||
"@types/node": "^12.12.64",
|
||||
"@typescript-eslint/eslint-plugin": "^2.34.0",
|
||||
"@typescript-eslint/parser": "^2.34.0",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-prettier": "^6.12.0",
|
||||
"eslint-plugin-import": "^2.22.1",
|
||||
"eslint-plugin-prettier": "^3.1.4",
|
||||
"jest": "^25.5.4",
|
||||
"jest-circus": "^25.5.4",
|
||||
"prettier": "^1.19.1",
|
||||
"ts-jest": "^26.4.1",
|
||||
"typescript": "^3.9.7"
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/node": "^24.1.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.57.0",
|
||||
"@typescript-eslint/parser": "^8.57.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"jest": "^29.7.0",
|
||||
"jest-circus": "^29.7.0",
|
||||
"prettier": "^3.3.3",
|
||||
"ts-jest": "^29.2.5",
|
||||
"typescript": "^5.5.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"config:base"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
export function getChannelId(channelName: string): number {
|
||||
if (channelName === 'stable') {
|
||||
return 0;
|
||||
} else if (channelName === 'beta') {
|
||||
return 1;
|
||||
} else if (channelName === 'dev') {
|
||||
return 2;
|
||||
} else if (channelName === 'canary') {
|
||||
return 3;
|
||||
} else {
|
||||
throw new Error(`Unexpected channel name: '${channelName}'.`);
|
||||
}
|
||||
}
|
||||
+126
-49
@@ -1,79 +1,156 @@
|
||||
import * as exec from '@actions/exec';
|
||||
|
||||
const EMULATOR_BOOT_TIMEOUT_SECONDS = 600;
|
||||
import * as fs from 'fs';
|
||||
|
||||
/**
|
||||
* Creates and launches a new AVD instance with the specified configurations.
|
||||
* Creates a new AVD instance with the specified configurations.
|
||||
*/
|
||||
export async function launchEmulator(apiLevel: number, target: string, arch: string, profile: string, avdName: string, emulatorOptions: string, disableAnimations: boolean): Promise<void> {
|
||||
// create a new AVD
|
||||
if (profile.trim() !== '') {
|
||||
console.log(`Creating AVD with custom profile ${profile}`);
|
||||
await exec.exec(`avdmanager create avd --force -n "${avdName}" --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 | avdmanager create avd --force -n "${avdName}" --abi '${target}/${arch}' --package 'system-images;android-${apiLevel};${target};${arch}'"`);
|
||||
}
|
||||
export async function createAvd(
|
||||
arch: string,
|
||||
avdName: string,
|
||||
cores: string,
|
||||
diskSize: string,
|
||||
enableHardwareKeyboard: boolean,
|
||||
forceAvdCreation: boolean,
|
||||
heapSize: string,
|
||||
profile: string,
|
||||
ramSize: string,
|
||||
sdcardPathOrSize: string,
|
||||
systemImageApiLevel: string,
|
||||
target: string,
|
||||
): Promise<void> {
|
||||
try {
|
||||
console.log(`::group::Create AVD`);
|
||||
// create a new AVD if AVD directory does not already exist or forceAvdCreation is true
|
||||
const avdPath = `${process.env.ANDROID_AVD_HOME}/${avdName}.avd`;
|
||||
if (!fs.existsSync(avdPath) || forceAvdCreation) {
|
||||
const profileOption = profile.trim() !== '' ? `--device '${profile}'` : '';
|
||||
const sdcardPathOrSizeOption = sdcardPathOrSize.trim() !== '' ? `--sdcard '${sdcardPathOrSize}'` : '';
|
||||
console.log(`Creating AVD.`);
|
||||
await exec.exec(
|
||||
`sh -c \\"echo no | avdmanager create avd --force -n "${avdName}" --package 'system-images;android-${systemImageApiLevel};${target};${arch}' ${profileOption} ${sdcardPathOrSizeOption}"`,
|
||||
);
|
||||
}
|
||||
|
||||
// start emulator
|
||||
console.log('Starting emulator.');
|
||||
if (cores || ramSize || heapSize || enableHardwareKeyboard || diskSize) {
|
||||
const configEntries: string[] = [];
|
||||
|
||||
// turn off hardware acceleration on Linux
|
||||
if (process.platform === 'linux') {
|
||||
emulatorOptions += ' -accel off';
|
||||
}
|
||||
if (cores) {
|
||||
configEntries.push(`hw.cpu.ncore=${cores}`);
|
||||
}
|
||||
if (ramSize) {
|
||||
configEntries.push(`hw.ramSize=${ramSize}`);
|
||||
}
|
||||
if (heapSize) {
|
||||
configEntries.push(`hw.heapSize=${heapSize}`);
|
||||
}
|
||||
if (enableHardwareKeyboard) {
|
||||
configEntries.push('hw.keyboard=yes');
|
||||
}
|
||||
if (diskSize) {
|
||||
configEntries.push(`disk.dataPartition.size=${diskSize}`);
|
||||
}
|
||||
|
||||
await exec.exec(`sh -c \\"${process.env.ANDROID_HOME}/emulator/emulator -avd "${avdName}" ${emulatorOptions} &"`, [], {
|
||||
listeners: {
|
||||
stderr: (data: Buffer) => {
|
||||
if (data.toString().includes('invalid command-line parameter')) {
|
||||
throw new Error(data.toString());
|
||||
}
|
||||
if (configEntries.length > 0) {
|
||||
const configContent = configEntries.join('\\n') + '\\n';
|
||||
await exec.exec(`sh -c \\"printf '${configContent}' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini"`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// wait for emulator to complete booting
|
||||
await waitForDevice();
|
||||
await exec.exec(`adb shell input keyevent 82`);
|
||||
|
||||
// disable animations
|
||||
if (disableAnimations) {
|
||||
console.log('Disabling animations.');
|
||||
await exec.exec(`adb shell settings put global window_animation_scale 0.0`);
|
||||
await exec.exec(`adb shell settings put global transition_animation_scale 0.0`);
|
||||
await exec.exec(`adb shell settings put global animator_duration_scale 0.0`);
|
||||
} finally {
|
||||
console.log(`::endgroup::`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Kills the running emulator on the defaut port.
|
||||
* Launches an existing AVD instance with the specified configurations.
|
||||
*/
|
||||
export async function killEmulator(): Promise<void> {
|
||||
export async function launchEmulator(
|
||||
avdName: string,
|
||||
disableAnimations: boolean,
|
||||
disableLinuxHardwareAcceleration: boolean,
|
||||
disableSpellChecker: boolean,
|
||||
emulatorBootTimeout: number,
|
||||
emulatorOptions: string,
|
||||
enableHardwareKeyboard: boolean,
|
||||
port: number,
|
||||
): Promise<void> {
|
||||
try {
|
||||
await exec.exec(`adb -s emulator-5554 emu kill`);
|
||||
} catch (error) {
|
||||
console.log(error.message);
|
||||
console.log(`::group::Launch Emulator`);
|
||||
|
||||
// turn off hardware acceleration on Linux
|
||||
if (process.platform === 'linux' && disableLinuxHardwareAcceleration) {
|
||||
console.log('Disabling Linux hardware acceleration.');
|
||||
emulatorOptions += ' -accel off';
|
||||
}
|
||||
|
||||
// start emulator
|
||||
console.log('Starting emulator.');
|
||||
|
||||
await exec.exec(`sh -c \\"${process.env.ANDROID_HOME}/emulator/emulator -port ${port} -avd "${avdName}" ${emulatorOptions} &"`, [], {
|
||||
listeners: {
|
||||
stderr: (data: Buffer) => {
|
||||
if (data.toString().includes('invalid command-line parameter')) {
|
||||
throw new Error(data.toString());
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// wait for emulator to complete booting
|
||||
await waitForDevice(port, emulatorBootTimeout);
|
||||
await adb(port, `shell input keyevent 82`);
|
||||
|
||||
if (disableAnimations) {
|
||||
console.log('Disabling animations.');
|
||||
await adb(port, `shell settings put global window_animation_scale 0.0`);
|
||||
await adb(port, `shell settings put global transition_animation_scale 0.0`);
|
||||
await adb(port, `shell settings put global animator_duration_scale 0.0`);
|
||||
}
|
||||
if (disableSpellChecker) {
|
||||
await adb(port, `shell settings put secure spell_checker_enabled 0`);
|
||||
}
|
||||
if (enableHardwareKeyboard) {
|
||||
await adb(port, `shell settings put secure show_ime_with_hard_keyboard 0`);
|
||||
}
|
||||
} finally {
|
||||
console.log(`::endgroup::`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Kills the running emulator on the default port.
|
||||
*/
|
||||
export async function killEmulator(port: number): Promise<void> {
|
||||
try {
|
||||
console.log(`::group::Terminate Emulator`);
|
||||
await adb(port, `emu kill`);
|
||||
} catch (error) {
|
||||
console.log(error instanceof Error ? error.message : error);
|
||||
} finally {
|
||||
console.log(`::endgroup::`);
|
||||
}
|
||||
}
|
||||
|
||||
async function adb(port: number, command: string): Promise<number> {
|
||||
return await exec.exec(`adb -s emulator-${port} ${command}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for emulator to boot.
|
||||
*/
|
||||
async function waitForDevice(): Promise<void> {
|
||||
async function waitForDevice(port: number, emulatorBootTimeout: number): Promise<void> {
|
||||
let booted = false;
|
||||
let attempts = 0;
|
||||
const retryInterval = 2; // retry every 2 seconds
|
||||
const maxAttemps = EMULATOR_BOOT_TIMEOUT_SECONDS / 2;
|
||||
const maxAttempts = emulatorBootTimeout / 2;
|
||||
while (!booted) {
|
||||
try {
|
||||
let result = '';
|
||||
await exec.exec(`adb shell getprop sys.boot_completed`, [], {
|
||||
await exec.exec(`adb -s emulator-${port} shell getprop sys.boot_completed`, [], {
|
||||
listeners: {
|
||||
stdout: (data: Buffer) => {
|
||||
result += data.toString();
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
if (result.trim() === '1') {
|
||||
console.log('Emulator booted.');
|
||||
@@ -81,10 +158,10 @@ async function waitForDevice(): Promise<void> {
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(error.message);
|
||||
console.warn(error instanceof Error ? error.message : error);
|
||||
}
|
||||
|
||||
if (attempts < maxAttemps) {
|
||||
if (attempts < maxAttempts) {
|
||||
await delay(retryInterval * 1000);
|
||||
} else {
|
||||
throw new Error(`Timeout waiting for emulator to boot.`);
|
||||
@@ -94,5 +171,5 @@ async function waitForDevice(): Promise<void> {
|
||||
}
|
||||
|
||||
function delay(ms: number) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
+71
-16
@@ -1,20 +1,15 @@
|
||||
export const MIN_API_LEVEL = 15;
|
||||
export const VALID_TARGETS: Array<string> = ['default', 'google_apis', 'google_apis_playstore'];
|
||||
export const VALID_ARCHS: Array<string> = ['x86', 'x86_64'];
|
||||
export const VALID_ARCHS: Array<string> = ['x86', 'x86_64', 'arm64-v8a'];
|
||||
export const VALID_CHANNELS: Array<string> = ['stable', 'beta', 'dev', 'canary'];
|
||||
export const MIN_PORT = 5554;
|
||||
export const MAX_PORT = 5584;
|
||||
|
||||
export function checkApiLevel(apiLevel: string): void {
|
||||
if (isNaN(Number(apiLevel)) || !Number.isInteger(Number(apiLevel))) {
|
||||
throw new Error(`Unexpected API level: '${apiLevel}'.`);
|
||||
}
|
||||
if (Number(apiLevel) < MIN_API_LEVEL) {
|
||||
throw new Error(`Minimum API level supported is ${MIN_API_LEVEL}.`);
|
||||
}
|
||||
}
|
||||
|
||||
export function checkTarget(target: string): void {
|
||||
if (!VALID_TARGETS.includes(target)) {
|
||||
throw new Error(`Value for input.target '${target}' is unknown. Supported options: ${VALID_TARGETS}.`);
|
||||
}
|
||||
export function playstoreTargetSubstitution(target: string): string {
|
||||
// "playstore" is an allowed shorthand for "google_apis_playstore" images
|
||||
// this is idempotent - return same even if run multiple times on same target
|
||||
if (target === 'playstore') return 'google_apis_playstore';
|
||||
if (target === 'playstore_ps16k') return 'google_apis_playstore_ps16k';
|
||||
return target;
|
||||
}
|
||||
|
||||
export function checkArch(arch: string): void {
|
||||
@@ -23,14 +18,74 @@ export function checkArch(arch: string): void {
|
||||
}
|
||||
}
|
||||
|
||||
export function checkChannel(channel: string): void {
|
||||
if (!VALID_CHANNELS.includes(channel)) {
|
||||
throw new Error(`Value for input.channel '${channel}' is unknown. Supported options: ${VALID_CHANNELS}.`);
|
||||
}
|
||||
}
|
||||
|
||||
export function checkForceAvdCreation(forceAvdCreation: string): void {
|
||||
if (!isValidBoolean(forceAvdCreation)) {
|
||||
throw new Error(`Input for input.force-avd-creation should be either 'true' or 'false'.`);
|
||||
}
|
||||
}
|
||||
|
||||
export function checkPort(port: number): void {
|
||||
if (port < MIN_PORT || port > MAX_PORT) {
|
||||
throw new Error(`Emulator port is outside of the supported port range [${MIN_PORT}, ${MAX_PORT}], was ${port}`);
|
||||
}
|
||||
if (port % 2 == 1) {
|
||||
throw new Error(`Emulator port has to be even, was ${port}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function checkDisableAnimations(disableAnimations: string): void {
|
||||
if (disableAnimations !== 'true' && disableAnimations !== 'false') {
|
||||
if (!isValidBoolean(disableAnimations)) {
|
||||
throw new Error(`Input for input.disable-animations should be either 'true' or 'false'.`);
|
||||
}
|
||||
}
|
||||
|
||||
export function checkDisableSpellchecker(disableSpellchecker: string): void {
|
||||
if (!isValidBoolean(disableSpellchecker)) {
|
||||
throw new Error(`Input for input.disable-spellchecker should be either 'true' or 'false'.`);
|
||||
}
|
||||
}
|
||||
|
||||
export function checkDisableLinuxHardwareAcceleration(disableLinuxHardwareAcceleration: string): void {
|
||||
if (!(isValidBoolean(disableLinuxHardwareAcceleration) || disableLinuxHardwareAcceleration === 'auto')) {
|
||||
throw new Error(`Input for input.disable-linux-hw-accel should be either 'true' or 'false' or 'auto'.`);
|
||||
}
|
||||
}
|
||||
|
||||
export function checkEnableHardwareKeyboard(enableHardwareKeyboard: string): void {
|
||||
if (!isValidBoolean(enableHardwareKeyboard)) {
|
||||
throw new Error(`Input for input.enable-hw-keyboard should be either 'true' or 'false'.`);
|
||||
}
|
||||
}
|
||||
|
||||
export function checkEmulatorBuild(emulatorBuild: string): void {
|
||||
if (isNaN(Number(emulatorBuild)) || !Number.isInteger(Number(emulatorBuild))) {
|
||||
throw new Error(`Unexpected emulator build: '${emulatorBuild}'.`);
|
||||
}
|
||||
}
|
||||
|
||||
function isValidBoolean(value: string): boolean {
|
||||
return value === 'true' || value === 'false';
|
||||
}
|
||||
|
||||
export function checkDiskSize(diskSize: string): void {
|
||||
// Disk size can be empty - the default value
|
||||
if (diskSize) {
|
||||
// Can also be number of bytes
|
||||
if (isNaN(Number(diskSize)) || !Number.isInteger(Number(diskSize))) {
|
||||
// Disk size can have a size multiplier at the end K, M or G
|
||||
const diskSizeUpperCase = diskSize.toUpperCase();
|
||||
if (diskSizeUpperCase.endsWith('K') || diskSizeUpperCase.endsWith('M') || diskSizeUpperCase.endsWith('G')) {
|
||||
const diskSizeNoModifier: string = diskSize.slice(0, -1);
|
||||
if (0 == diskSizeNoModifier.length || isNaN(Number(diskSizeNoModifier)) || !Number.isInteger(Number(diskSizeNoModifier))) {
|
||||
throw new Error(`Unexpected disk size: '${diskSize}'.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+138
-18
@@ -1,33 +1,58 @@
|
||||
import * as core from '@actions/core';
|
||||
import { installAndroidSdk } from './sdk-installer';
|
||||
import { checkApiLevel, checkTarget, checkArch, checkDisableAnimations, checkEmulatorBuild } from './input-validator';
|
||||
import { launchEmulator, killEmulator } from './emulator-manager';
|
||||
import {
|
||||
checkArch,
|
||||
checkDisableAnimations,
|
||||
checkEmulatorBuild,
|
||||
checkDisableSpellchecker,
|
||||
checkDisableLinuxHardwareAcceleration,
|
||||
checkForceAvdCreation,
|
||||
checkChannel,
|
||||
checkEnableHardwareKeyboard,
|
||||
checkDiskSize,
|
||||
checkPort,
|
||||
playstoreTargetSubstitution,
|
||||
MIN_PORT,
|
||||
} from './input-validator';
|
||||
import { createAvd, launchEmulator, killEmulator } from './emulator-manager';
|
||||
import * as exec from '@actions/exec';
|
||||
import { parseScript } from './script-parser';
|
||||
import { getChannelId } from './channel-id-mapper';
|
||||
import { accessSync, constants } from 'fs';
|
||||
|
||||
async function run() {
|
||||
let port: number = MIN_PORT;
|
||||
try {
|
||||
console.log(`::group::Configure emulator`);
|
||||
let linuxSupportKVM = false;
|
||||
// only support running on macOS or Linux
|
||||
if (process.platform !== 'darwin') {
|
||||
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.`
|
||||
);
|
||||
try {
|
||||
accessSync('/dev/kvm', constants.R_OK | constants.W_OK);
|
||||
linuxSupportKVM = true;
|
||||
} catch {
|
||||
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 });
|
||||
checkApiLevel(apiLevelInput);
|
||||
const apiLevel = Number(apiLevelInput);
|
||||
const apiLevel = core.getInput('api-level', { required: true });
|
||||
console.log(`API level: ${apiLevel}`);
|
||||
|
||||
let systemImageApiLevel = core.getInput('system-image-api-level');
|
||||
if (!systemImageApiLevel) {
|
||||
systemImageApiLevel = apiLevel;
|
||||
}
|
||||
console.log(`System image API level: ${systemImageApiLevel}`);
|
||||
|
||||
// target of the system image
|
||||
const targetInput = core.getInput('target');
|
||||
const target = targetInput == 'playstore' ? 'google_apis_playstore' : targetInput;
|
||||
checkTarget(target);
|
||||
const target = playstoreTargetSubstitution(core.getInput('target'));
|
||||
console.log(`target: ${target}`);
|
||||
|
||||
// CPU architecture of the system image
|
||||
@@ -39,10 +64,45 @@ async function run() {
|
||||
const profile = core.getInput('profile');
|
||||
console.log(`Hardware profile: ${profile}`);
|
||||
|
||||
// Number of cores to use for emulator
|
||||
const cores = core.getInput('cores');
|
||||
console.log(`Cores: ${cores}`);
|
||||
|
||||
// RAM to use for AVD
|
||||
const ramSize = core.getInput('ram-size');
|
||||
console.log(`RAM size: ${ramSize}`);
|
||||
|
||||
// Heap size to use for AVD
|
||||
const heapSize = core.getInput('heap-size');
|
||||
console.log(`Heap size: ${heapSize}`);
|
||||
|
||||
// SD card path or size used for creating the AVD
|
||||
const sdcardPathOrSize = core.getInput('sdcard-path-or-size');
|
||||
console.log(`SD card path or size: ${sdcardPathOrSize}`);
|
||||
|
||||
const diskSize = core.getInput('disk-size');
|
||||
checkDiskSize(diskSize);
|
||||
console.log(`Disk size: ${diskSize}`);
|
||||
|
||||
// custom name used for creating the AVD
|
||||
const avdName = core.getInput('avd-name');
|
||||
console.log(`AVD name: ${avdName}`);
|
||||
|
||||
// force AVD creation
|
||||
const forceAvdCreationInput = core.getInput('force-avd-creation');
|
||||
checkForceAvdCreation(forceAvdCreationInput);
|
||||
const forceAvdCreation = forceAvdCreationInput === 'true';
|
||||
console.log(`force avd creation: ${forceAvdCreation}`);
|
||||
|
||||
// Emulator boot timeout seconds
|
||||
const emulatorBootTimeout = parseInt(core.getInput('emulator-boot-timeout'), 10);
|
||||
console.log(`Emulator boot timeout: ${emulatorBootTimeout}`);
|
||||
|
||||
// Emulator port to use
|
||||
port = parseInt(core.getInput('emulator-port'), 10);
|
||||
checkPort(port);
|
||||
console.log(`emulator port: ${port}`);
|
||||
|
||||
// emulator options
|
||||
const emulatorOptions = core.getInput('emulator-options').trim();
|
||||
console.log(`emulator options: ${emulatorOptions}`);
|
||||
@@ -53,6 +113,27 @@ async function run() {
|
||||
const disableAnimations = disableAnimationsInput === 'true';
|
||||
console.log(`disable animations: ${disableAnimations}`);
|
||||
|
||||
// disable spellchecker
|
||||
const disableSpellcheckerInput = core.getInput('disable-spellchecker');
|
||||
checkDisableSpellchecker(disableSpellcheckerInput);
|
||||
const disableSpellchecker = disableSpellcheckerInput === 'true';
|
||||
console.log(`disable spellchecker: ${disableSpellchecker}`);
|
||||
|
||||
// disable linux hardware acceleration
|
||||
let disableLinuxHardwareAccelerationInput = core.getInput('disable-linux-hw-accel');
|
||||
checkDisableLinuxHardwareAcceleration(disableLinuxHardwareAccelerationInput);
|
||||
if (disableLinuxHardwareAccelerationInput === 'auto' && process.platform === 'linux') {
|
||||
disableLinuxHardwareAccelerationInput = linuxSupportKVM ? 'false' : 'true';
|
||||
}
|
||||
const disableLinuxHardwareAcceleration = disableLinuxHardwareAccelerationInput === 'true';
|
||||
console.log(`disable Linux hardware acceleration: ${disableLinuxHardwareAcceleration}`);
|
||||
|
||||
// enable hardware keyboard
|
||||
const enableHardwareKeyboardInput = core.getInput('enable-hw-keyboard');
|
||||
checkEnableHardwareKeyboard(enableHardwareKeyboardInput);
|
||||
const enableHardwareKeyboard = enableHardwareKeyboardInput === 'true';
|
||||
console.log(`enable hardware keyboard: ${enableHardwareKeyboard}`);
|
||||
|
||||
// emulator build
|
||||
const emulatorBuildInput = core.getInput('emulator-build');
|
||||
if (emulatorBuildInput) {
|
||||
@@ -82,6 +163,12 @@ async function run() {
|
||||
}
|
||||
const cmakeVersion = !cmakeInput ? undefined : cmakeInput;
|
||||
|
||||
// channelId (up to and including) to download the SDK packages from
|
||||
const channelName = core.getInput('channel');
|
||||
checkChannel(channelName);
|
||||
const channelId = getChannelId(channelName);
|
||||
console.log(`Channel: ${channelId} (${channelName})`);
|
||||
|
||||
// custom script to run
|
||||
const scriptInput = core.getInput('script', { required: true });
|
||||
const scripts = parseScript(scriptInput);
|
||||
@@ -90,11 +177,40 @@ async function run() {
|
||||
console.log(`${script}`);
|
||||
});
|
||||
|
||||
// custom pre emulator launch script
|
||||
const preEmulatorLaunchScriptInput = core.getInput('pre-emulator-launch-script');
|
||||
const preEmulatorLaunchScripts = !preEmulatorLaunchScriptInput ? undefined : parseScript(preEmulatorLaunchScriptInput);
|
||||
console.log(`Pre emulator launch script:`);
|
||||
preEmulatorLaunchScripts?.forEach(async (script: string) => {
|
||||
console.log(`${script}`);
|
||||
});
|
||||
console.log(`::endgroup::`);
|
||||
|
||||
// install SDK
|
||||
await installAndroidSdk(apiLevel, target, arch, emulatorBuild, ndkVersion, cmakeVersion);
|
||||
await installAndroidSdk(apiLevel, systemImageApiLevel, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion);
|
||||
|
||||
// create AVD
|
||||
await createAvd(arch, avdName, cores, diskSize, enableHardwareKeyboard, forceAvdCreation, heapSize, profile, ramSize, sdcardPathOrSize, systemImageApiLevel, target);
|
||||
|
||||
// execute pre emulator launch script if set
|
||||
if (preEmulatorLaunchScripts !== undefined) {
|
||||
console.log(`::group::Run pre emulator launch script`);
|
||||
try {
|
||||
for (const preEmulatorLaunchScript of preEmulatorLaunchScripts) {
|
||||
// use array form to avoid various quote escaping problems
|
||||
// caused by exec(`sh -c "${preEmulatorLaunchScript}"`)
|
||||
await exec.exec('sh', ['-c', preEmulatorLaunchScript], {
|
||||
cwd: workingDirectory,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
core.setFailed(error instanceof Error ? error.message : (error as string));
|
||||
}
|
||||
console.log(`::endgroup::`);
|
||||
}
|
||||
|
||||
// launch an emulator
|
||||
await launchEmulator(apiLevel, target, arch, profile, avdName, emulatorOptions, disableAnimations);
|
||||
await launchEmulator(avdName, disableAnimations, disableLinuxHardwareAcceleration, disableSpellchecker, emulatorBootTimeout, emulatorOptions, enableHardwareKeyboard, port);
|
||||
|
||||
// execute the custom script
|
||||
try {
|
||||
@@ -103,18 +219,22 @@ async function run() {
|
||||
process.chdir(workingDirectory);
|
||||
}
|
||||
for (const script of scripts) {
|
||||
await exec.exec(`sh -c \\"${script}"`);
|
||||
// use array form to avoid various quote escaping problems
|
||||
// caused by exec(`sh -c "${script}"`)
|
||||
await exec.exec('sh', ['-c', script], {
|
||||
env: { ...process.env, EMULATOR_PORT: `${port}`, ANDROID_SERIAL: `emulator-${port}` },
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
core.setFailed(error.message);
|
||||
core.setFailed(error instanceof Error ? error.message : (error as string));
|
||||
}
|
||||
|
||||
// finally kill the emulator
|
||||
await killEmulator();
|
||||
await killEmulator(port);
|
||||
} catch (error) {
|
||||
// kill the emulator so the action can exit
|
||||
await killEmulator();
|
||||
core.setFailed(error.message);
|
||||
await killEmulator(port);
|
||||
core.setFailed(error instanceof Error ? error.message : (error as string));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+71
-49
@@ -1,70 +1,92 @@
|
||||
import * as core from '@actions/core';
|
||||
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 = '30.0.2';
|
||||
const CMDLINE_TOOLS_URL_MAC = 'https://dl.google.com/android/repository/commandlinetools-mac-6609375_latest.zip';
|
||||
const CMDLINE_TOOLS_URL_LINUX = 'https://dl.google.com/android/repository/commandlinetools-linux-6609375_latest.zip';
|
||||
const BUILD_TOOLS_VERSION = '37.0.0';
|
||||
// SDK command-line tools 23.0
|
||||
const CMDLINE_TOOLS_VERSION = '16111833';
|
||||
const CMDLINE_TOOLS_URL_MAC = `https://dl.google.com/android/repository/commandlinetools-mac_x86_64-${CMDLINE_TOOLS_VERSION}_latest.zip`;
|
||||
const CMDLINE_TOOLS_URL_LINUX = `https://dl.google.com/android/repository/commandlinetools-linux-${CMDLINE_TOOLS_VERSION}_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, ndkVersion?: string, cmakeVersion?: string): Promise<void> {
|
||||
const isOnMac = process.platform === 'darwin';
|
||||
export async function installAndroidSdk(
|
||||
apiLevel: string,
|
||||
systemImageApiLevel: String,
|
||||
target: string,
|
||||
arch: string,
|
||||
channelId: number,
|
||||
emulatorBuild?: string,
|
||||
ndkVersion?: string,
|
||||
cmakeVersion?: string,
|
||||
): Promise<void> {
|
||||
try {
|
||||
console.log(`::group::Install Android SDK`);
|
||||
const isOnMac = process.platform === 'darwin';
|
||||
const isArm = process.arch === 'arm64';
|
||||
|
||||
if (!isOnMac) {
|
||||
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(`${process.env.ANDROID_HOME}/cmdline-tools`);
|
||||
await exec.exec(`curl -fo commandlinetools.zip ${sdkUrl}`);
|
||||
await exec.exec(`unzip -q commandlinetools.zip -d ${cmdlineToolsPath}`);
|
||||
await io.rmRF('commandlinetools.zip');
|
||||
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;
|
||||
const downloadPath = await tc.downloadTool(sdkUrl);
|
||||
await tc.extractZip(downloadPath, cmdlineToolsPath);
|
||||
await io.mv(`${cmdlineToolsPath}/cmdline-tools`, `${cmdlineToolsPath}/latest`);
|
||||
}
|
||||
|
||||
// add paths for commandline-tools and platform-tools
|
||||
core.addPath(`${cmdlineToolsPath}/tools:${cmdlineToolsPath}/tools/bin:${process.env.ANDROID_HOME}/platform-tools`);
|
||||
}
|
||||
core.addPath(`${cmdlineToolsPath}/latest:${cmdlineToolsPath}/latest/bin:${process.env.ANDROID_HOME}/platform-tools`);
|
||||
|
||||
// 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');
|
||||
}
|
||||
// set standard AVD path
|
||||
await io.mkdirP(`${process.env.HOME}/.android/avd`);
|
||||
core.exportVariable('ANDROID_AVD_HOME', `${process.env.HOME}/.android/avd`);
|
||||
|
||||
// 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');
|
||||
}
|
||||
// accept all Android SDK licenses
|
||||
await exec.exec(`sh -c \\"yes | sdkmanager --licenses > /dev/null"`);
|
||||
|
||||
console.log('Installing latest build tools, platform tools, and platform.');
|
||||
console.log('Installing latest build tools, platform tools, and platform.');
|
||||
|
||||
await exec.exec(`sh -c \\"sdkmanager --install 'build-tools;${BUILD_TOOLS_VERSION}' platform-tools 'platforms;android-${apiLevel}'> /dev/null"`);
|
||||
|
||||
await exec.exec(`sh -c \\"sdkmanager --install 'build-tools;${BUILD_TOOLS_VERSION}' platform-tools 'platforms;android-${apiLevel}' > /dev/null"`);
|
||||
if (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 io.rmRF(`${process.env.ANDROID_HOME}/emulator`);
|
||||
await exec.exec(`unzip -q emulator.zip -d ${process.env.ANDROID_HOME}`);
|
||||
await io.rmRF('emulator.zip');
|
||||
} else {
|
||||
console.log('Installing latest emulator.');
|
||||
await exec.exec(`sh -c \\"sdkmanager --install emulator > /dev/null"`);
|
||||
}
|
||||
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 emulator --channel=${channelId} > /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"`);
|
||||
if (emulatorBuild) {
|
||||
console.log(`Installing emulator build ${emulatorBuild}.`);
|
||||
// TODO find out the correct download URLs for all build ids
|
||||
var downloadUrlSuffix: string;
|
||||
const majorBuildVersion = Number(emulatorBuild);
|
||||
if (majorBuildVersion >= 8000000) {
|
||||
if (isArm) {
|
||||
downloadUrlSuffix = `_aarch64-${emulatorBuild}`;
|
||||
} else {
|
||||
downloadUrlSuffix = `_x64-${emulatorBuild}`;
|
||||
}
|
||||
} else if (majorBuildVersion >= 7000000) {
|
||||
downloadUrlSuffix = `_x64-${emulatorBuild}`;
|
||||
} else {
|
||||
downloadUrlSuffix = `-${emulatorBuild}`;
|
||||
}
|
||||
await exec.exec(`curl -fo emulator.zip https://dl.google.com/android/repository/emulator-${isOnMac ? 'darwin' : 'linux'}${downloadUrlSuffix}.zip`);
|
||||
await exec.exec(`unzip -o -q emulator.zip -d ${process.env.ANDROID_HOME}`);
|
||||
await io.rmRF('emulator.zip');
|
||||
}
|
||||
console.log('Installing system images.');
|
||||
await exec.exec(`sh -c \\"sdkmanager --install 'system-images;android-${systemImageApiLevel};${target};${arch}' --channel=${channelId} > /dev/null"`);
|
||||
|
||||
if (ndkVersion) {
|
||||
console.log(`Installing NDK ${ndkVersion}.`);
|
||||
await exec.exec(`sh -c \\"sdkmanager --install 'ndk;${ndkVersion}' --channel=${channelId} > /dev/null"`);
|
||||
}
|
||||
if (cmakeVersion) {
|
||||
console.log(`Installing CMake ${cmakeVersion}.`);
|
||||
await exec.exec(`sh -c \\"sdkmanager --install 'cmake;${cmakeVersion}' --channel=${channelId} > /dev/null"`);
|
||||
}
|
||||
} finally {
|
||||
console.log(`::endgroup::`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
.gradle
|
||||
build/
|
||||
target/
|
||||
out/
|
||||
local.properties
|
||||
.idea/
|
||||
*.iml
|
||||
*.DS_Store
|
||||
@@ -1,36 +0,0 @@
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
buildToolsVersion "30.0.2"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.example.testapp"
|
||||
minSdkVersion 15
|
||||
targetSdkVersion 30
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.3.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
testImplementation 'junit:junit:4.13'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.example.testapp"
|
||||
compileSdk {
|
||||
version = release(37) {
|
||||
minorApiLevel = 1
|
||||
}
|
||||
}
|
||||
buildToolsVersion = "37.0.0"
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.example.testapp"
|
||||
minSdk = 23
|
||||
targetSdk = 37
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
|
||||
compilerOptions {
|
||||
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("androidx.appcompat:appcompat:1.8.0")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.3.0")
|
||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.7.0")
|
||||
}
|
||||
Vendored
-21
@@ -1,21 +0,0 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.testapp">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
@@ -9,7 +8,9 @@
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity android:name="com.example.testapp.MainActivity">
|
||||
<activity
|
||||
android:name="com.example.testapp.MainActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
@@ -10,9 +10,6 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Hello World!"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
android:layout_gravity="center" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</FrameLayout>
|
||||
@@ -1,27 +0,0 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.4.0'
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.0.1'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
plugins {
|
||||
id("com.android.application") version "9.3.2" apply false
|
||||
id("org.jetbrains.kotlin.android") version "2.4.10" apply false
|
||||
}
|
||||
|
||||
@@ -1,40 +1,7 @@
|
||||
org.gradle.parallel=true
|
||||
org.gradle.configureondemand=true
|
||||
org.gradle.caching=true
|
||||
org.gradle.vfs.watch=true
|
||||
|
||||
# Enable Kotlin incremental compilation
|
||||
kotlin.incremental=true
|
||||
|
||||
# Enable parallel tasks execution for Kotlin Gradle plugin
|
||||
kotlin.parallel.tasks.in.project=true
|
||||
|
||||
# Kotlin code style
|
||||
kotlin.code.style=official
|
||||
|
||||
# Run kapt directly using Gradle workers
|
||||
kapt.use.worker.api=true
|
||||
|
||||
# Enable incremental annotation processor for KAPT
|
||||
kapt.incremental.apt=true
|
||||
|
||||
# Turn off AP discovery in compile path to enable compile avoidance
|
||||
kapt.include.compile.classpath=false
|
||||
|
||||
# Use R8 instead of ProGuard for code shrinking.
|
||||
android.enableR8.fullMode=true
|
||||
|
||||
# Enable gradle workers
|
||||
android.enableGradleWorkers=true
|
||||
|
||||
# Enable AndroidX
|
||||
android.useAndroidX=true
|
||||
|
||||
# Enable rudimentary R class namespacing where each library only contains
|
||||
# references to the resources it declares instead of declarations plus all
|
||||
# transitive dependency references.
|
||||
android.namespacedRClass=true
|
||||
|
||||
# Only keep the single relevant constructor for types mentioned in XML files
|
||||
# instead of using a parameter wildcard which keeps them all.
|
||||
android.useMinimalKeepRules=true
|
||||
|
||||
BIN
Binary file not shown.
+5
-2
@@ -1,6 +1,9 @@
|
||||
#Tue Nov 26 18:34:05 AEDT 2019
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.1-bin.zip
|
||||
networkTimeout=10000
|
||||
retries=0
|
||||
retryBackOffMs=500
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
|
||||
|
||||
Vendored
+193
-117
@@ -1,78 +1,128 @@
|
||||
#!/usr/bin/env sh
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
#
|
||||
# gradlew start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh gradlew
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
@@ -81,92 +131,118 @@ Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
JAVACMD=java
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
||||
Vendored
+49
-51
@@ -1,84 +1,82 @@
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem gradlew startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
@rem Set local scope for the variables, and ensure extensions are enabled
|
||||
setlocal EnableExtensions
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
"%COMSPEC%" /c exit 1
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
"%COMSPEC%" /c exit 1
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
@rem Execute gradlew
|
||||
@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
|
||||
@rem which allows us to clear the local environment before executing the java command
|
||||
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
:exitWithErrorLevel
|
||||
@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
|
||||
"%COMSPEC%" /c exit %ERRORLEVEL%
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
rootProject.name='Test Project'
|
||||
include ':app'
|
||||
@@ -0,0 +1,28 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
google {
|
||||
content {
|
||||
includeGroupByRegex("androidx.*")
|
||||
includeGroupByRegex("com.android.*")
|
||||
includeGroupByRegex("com.google.*")
|
||||
}
|
||||
}
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "Test Project"
|
||||
include(":app")
|
||||
|
||||
dependencyResolutionManagement {
|
||||
repositories {
|
||||
google {
|
||||
content {
|
||||
includeGroupByRegex("androidx.*")
|
||||
includeGroupByRegex("com.android.*")
|
||||
includeGroupByRegex("com.google.*")
|
||||
}
|
||||
}
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
+3
-2
@@ -2,7 +2,7 @@
|
||||
"compilerOptions": {
|
||||
/* Basic Options */
|
||||
// "incremental": true, /* Enable incremental compilation */
|
||||
"target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
|
||||
"target": "es2022", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
|
||||
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
|
||||
// "allowJs": true, /* Allow javascript files to be compiled. */
|
||||
// "checkJs": true, /* Report errors in .js files. */
|
||||
@@ -45,7 +45,8 @@
|
||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||
// "types": [], /* Type declaration files to be included in compilation. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||
"skipLibCheck": true
|
||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user