From 3b0137e329d512d4e853a75a200d702182d9d92e Mon Sep 17 00:00:00 2001 From: Timothy <6560631+TimoPtr@users.noreply.github.com> Date: Mon, 17 Mar 2025 12:48:02 +0100 Subject: [PATCH 1/7] Add missing targets --- .github/workflows/manually.yml | 2 +- README.md | 2 +- __tests__/input-validator.test.ts | 15 +++++++++++++++ action-types.yml | 3 +++ action.yml | 2 +- lib/input-validator.js | 2 +- src/input-validator.ts | 2 +- 7 files changed, 23 insertions(+), 5 deletions(-) diff --git a/.github/workflows/manually.yml b/.github/workflows/manually.yml index a74e1b3dd..d52dd7d0a 100644 --- a/.github/workflows/manually.yml +++ b/.github/workflows/manually.yml @@ -11,7 +11,7 @@ on: required: true default: '34' target: - description: 'target of the system image - default, google_apis, google_apis_playstore, aosp_atd, google_atd, android-wear, android-wear-cn, android-tv or google-tv' + 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, andrdoid-automotive, android-automotive-playstore or android-desktop' required: true default: 'default' arch: diff --git a/README.md b/README.md index 980879213..d1bca2ced 100644 --- a/README.md +++ b/README.md @@ -180,7 +180,7 @@ jobs: | **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`, `playstore`, `android-wear`, `android-wear-cn`, `android-tv`, `google-tv`, `aosp_atd` or `google_atd`. Note that `aosp_atd` and `google_atd` currently require the following: `api-level: 30`, `arch: x86` or `arch: arm64-v8` and `channel: canary`. | +| `target` | Optional | `default` | Target of the system image - `default`, `google_apis`, `playstore`, `android-wear`, `android-wear-cn`, `android-tv`, `google-tv`, `aosp_atd`, `google_atd`, `andrdoid-automotive`, `android-automotive-playstore` or `android-desktop`. Note that `aosp_atd` and `google_atd` currently require the following: `api-level: 30`, `arch: x86` or `arch: arm64-v8` and `channel: canary`. | | `arch` | Optional | `x86` | CPU architecture of the system image - `x86`, `x86_64` or `arm64-v8a`. Note that `x86_64` image is only available for API 21+. `arm64-v8a` images require Android 4.2+ and are limited to fewer API levels (e.g. 30). | | `profile` | Optional | N/A | Hardware profile used for creating the AVD - e.g. `Nexus 6`. For a list of all profiles available, run `avdmanager list device`. | | `cores` | Optional | 2 | Number of cores to use for the emulator (`hw.cpu.ncore` in config.ini). | diff --git a/__tests__/input-validator.test.ts b/__tests__/input-validator.test.ts index b08d5f5d3..dec4c1d6e 100644 --- a/__tests__/input-validator.test.ts +++ b/__tests__/input-validator.test.ts @@ -97,6 +97,21 @@ describe('target validator tests', () => { validator.checkTarget('google-tv'); }; expect(func9).not.toThrow(); + + const func10 = () => { + validator.checkTarget('andrdoid-automotive'); + }; + expect(func10).not.toThrow(); + + const func11 = () => { + validator.checkTarget('android-automotive-playstore'); + }; + expect(func11).not.toThrow(); + + const func12 = () => { + validator.checkTarget('android-desktop'); + }; + expect(func12).not.toThrow(); }); }); diff --git a/action-types.yml b/action-types.yml index b77d4b11d..ecdc5ee5d 100644 --- a/action-types.yml +++ b/action-types.yml @@ -13,6 +13,9 @@ inputs: - android-wear-cn - android-tv - google-tv + - andrdoid-automotive + - android-automotive-playstore + - android-desktop arch: type: enum allowed-values: diff --git a/action.yml b/action.yml index fa60d74dd..663797023 100644 --- a/action.yml +++ b/action.yml @@ -9,7 +9,7 @@ inputs: description: 'API level of the platform and system image - e.g. 23 for Android Marshmallow, 29 for Android 10' required: true target: - description: 'target of the system image - default, google_apis, google_apis_playstore, aosp_atd, google_atd, android-wear, android-wear-cn, android-tv or google-tv' + 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, andrdoid-automotive, android-automotive-playstore or android-desktop' default: 'default' arch: description: 'CPU architecture of the system image - x86, x86_64 or arm64-v8a' diff --git a/lib/input-validator.js b/lib/input-validator.js index 626ada89d..6ba796361 100644 --- a/lib/input-validator.js +++ b/lib/input-validator.js @@ -2,7 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.checkDiskSize = exports.checkEmulatorBuild = exports.checkEnableHardwareKeyboard = exports.checkDisableLinuxHardwareAcceleration = exports.checkDisableSpellchecker = exports.checkDisableAnimations = exports.checkPort = exports.checkForceAvdCreation = exports.checkChannel = exports.checkArch = exports.checkTarget = exports.checkApiLevel = exports.PREVIEW_API_LEVELS = exports.MAX_PORT = exports.MIN_PORT = exports.VALID_CHANNELS = exports.VALID_ARCHS = exports.VALID_TARGETS = exports.MIN_API_LEVEL = void 0; exports.MIN_API_LEVEL = 15; -exports.VALID_TARGETS = ['default', 'google_apis', 'aosp_atd', 'google_atd', 'google_apis_playstore', 'android-wear', 'android-wear-cn', 'android-tv', 'google-tv']; +exports.VALID_TARGETS = ['default', 'google_apis', 'aosp_atd', 'google_atd', 'google_apis_playstore', 'android-wear', 'android-wear-cn', 'android-tv', 'google-tv', 'andrdoid-automotive', 'android-automotive-playstore', 'android-desktop']; exports.VALID_ARCHS = ['x86', 'x86_64', 'arm64-v8a']; exports.VALID_CHANNELS = ['stable', 'beta', 'dev', 'canary']; exports.MIN_PORT = 5554; diff --git a/src/input-validator.ts b/src/input-validator.ts index e5142ccc3..b62aa64e4 100644 --- a/src/input-validator.ts +++ b/src/input-validator.ts @@ -1,5 +1,5 @@ export const MIN_API_LEVEL = 15; -export const VALID_TARGETS: Array = ['default', 'google_apis', 'aosp_atd', 'google_atd', 'google_apis_playstore', 'android-wear', 'android-wear-cn', 'android-tv', 'google-tv']; +export const VALID_TARGETS: Array = ['default', 'google_apis', 'aosp_atd', 'google_atd', 'google_apis_playstore', 'android-wear', 'android-wear-cn', 'android-tv', 'google-tv', 'andrdoid-automotive', 'android-automotive-playstore', 'android-desktop']; export const VALID_ARCHS: Array = ['x86', 'x86_64', 'arm64-v8a']; export const VALID_CHANNELS: Array = ['stable', 'beta', 'dev', 'canary']; export const MIN_PORT = 5554; From cae5ace128e44b5b38f285732218205257a4992e Mon Sep 17 00:00:00 2001 From: Timothy <6560631+TimoPtr@users.noreply.github.com> Date: Mon, 17 Mar 2025 13:48:37 +0100 Subject: [PATCH 2/7] Fix codestyle --- src/input-validator.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/input-validator.ts b/src/input-validator.ts index b62aa64e4..f4f70475b 100644 --- a/src/input-validator.ts +++ b/src/input-validator.ts @@ -1,5 +1,17 @@ export const MIN_API_LEVEL = 15; -export const VALID_TARGETS: Array = ['default', 'google_apis', 'aosp_atd', 'google_atd', 'google_apis_playstore', 'android-wear', 'android-wear-cn', 'android-tv', 'google-tv', 'andrdoid-automotive', 'android-automotive-playstore', 'android-desktop']; +export const VALID_TARGETS: Array = [ + 'default', + 'google_apis', + 'aosp_atd', + 'google_atd', + 'google_apis_playstore', + 'android-wear', + 'android-wear-cn', + 'android-tv', + 'google-tv', + 'andrdoid-automotive', + 'android-automotive-playstore', + 'android-desktop']; export const VALID_ARCHS: Array = ['x86', 'x86_64', 'arm64-v8a']; export const VALID_CHANNELS: Array = ['stable', 'beta', 'dev', 'canary']; export const MIN_PORT = 5554; From 606f1afdf1644a55e2911b2d32a6989682023b3f Mon Sep 17 00:00:00 2001 From: Timothy <6560631+TimoPtr@users.noreply.github.com> Date: Tue, 18 Mar 2025 14:23:25 +0100 Subject: [PATCH 3/7] Add support for SDKExtension --- .github/workflows/main.yml | 7 +++++++ .github/workflows/manually.yml | 3 +++ README.md | 27 +++++++++++++++++++++++++++ __tests__/input-validator.test.ts | 30 ++++++++++++++++++++++++++++++ action-types.yml | 2 ++ action.yml | 3 +++ lib/emulator-manager.js | 5 +++-- lib/input-validator.js | 26 ++++++++++++++++++++++++-- lib/main.js | 10 ++++++++-- lib/sdk-installer.js | 7 ++++--- src/emulator-manager.ts | 4 +++- src/input-validator.ts | 12 +++++++++++- src/main.ts | 11 ++++++++++- src/sdk-installer.ts | 16 +++++++++++++--- 14 files changed, 148 insertions(+), 15 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 587da8638..c6720a137 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -39,6 +39,11 @@ jobs: api-level: 35 target: google_apis arch: x86_64 + - os: ubuntu-latest + api-level: 34 + target: android-automotive + arch: x86_64 + sdk-extension: 9 steps: - name: checkout @@ -85,6 +90,7 @@ jobs: api-level: ${{ matrix.api-level }} target: ${{ matrix.target }} arch: ${{ matrix.arch }} + sdk-extension: ${{ matrix.sdk-extension }} profile: Galaxy Nexus cores: 2 sdcard-path-or-size: 100M @@ -102,6 +108,7 @@ jobs: api-level: ${{ matrix.api-level }} target: ${{ matrix.target }} arch: ${{ matrix.arch }} + sdk-extension: ${{ matrix.sdk-extension }} profile: Galaxy Nexus cores: 2 ram-size: 2048M diff --git a/.github/workflows/manually.yml b/.github/workflows/manually.yml index d52dd7d0a..58140272e 100644 --- a/.github/workflows/manually.yml +++ b/.github/workflows/manually.yml @@ -10,6 +10,8 @@ on: description: 'API level of the platform and system image' required: true default: '34' + sdk-extension: + description: 'SDK extension of a given api level' 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, andrdoid-automotive, android-automotive-playstore or android-desktop' required: true @@ -68,6 +70,7 @@ jobs: api-level: ${{ github.event.inputs.api-level }} target: ${{ github.event.inputs.target }} arch: ${{ github.event.inputs.arch }} + sdk-extension: ${{ github.event.inputs.sdk-extension }} profile: Galaxy Nexus emulator-options: ${{ github.event.inputs.emulator-options }} emulator-build: ${{ github.event.inputs.emulator-build }} diff --git a/README.md b/README.md index d1bca2ced..db31f1441 100644 --- a/README.md +++ b/README.md @@ -119,6 +119,31 @@ jobs: script: ./gradlew connectedCheck ``` +If you need a specific [SDKExtension](https://developer.android.com/guide/sdk-extensions) + +```yml +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@v4 + + - 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 + sdk-extension: 9 + 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@v4` step for caching Gradle, more details see [#229](https://github.com/ReactiveCircus/android-emulator-runner/issues/229) @@ -180,6 +205,7 @@ jobs: | **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**. | +| `sdk-extension` | Optional | N/A | SDK extension of a given api level - e.g. 9 for Android API 34 it will translate to 34-ext9. Check https://developer.android.com/guide/sdk-extensions for more details. `-ext` should not be part of the input, **the input should be an integer**. | | `target` | Optional | `default` | Target of the system image - `default`, `google_apis`, `playstore`, `android-wear`, `android-wear-cn`, `android-tv`, `google-tv`, `aosp_atd`, `google_atd`, `andrdoid-automotive`, `android-automotive-playstore` or `android-desktop`. Note that `aosp_atd` and `google_atd` currently require the following: `api-level: 30`, `arch: x86` or `arch: arm64-v8` and `channel: canary`. | | `arch` | Optional | `x86` | CPU architecture of the system image - `x86`, `x86_64` or `arm64-v8a`. Note that `x86_64` image is only available for API 21+. `arm64-v8a` images require Android 4.2+ and are limited to fewer API levels (e.g. 30). | | `profile` | Optional | N/A | Hardware profile used for creating the AVD - e.g. `Nexus 6`. For a list of all profiles available, run `avdmanager list device`. | @@ -243,5 +269,6 @@ These are some of the open-source projects using (or used) **Android Emulator Ru - [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) If you are using **Android Emulator Runner** and want your project included in the list, please feel free to open a pull request. diff --git a/__tests__/input-validator.test.ts b/__tests__/input-validator.test.ts index dec4c1d6e..0f7be5d24 100644 --- a/__tests__/input-validator.test.ts +++ b/__tests__/input-validator.test.ts @@ -44,6 +44,36 @@ describe('api-level validator tests', () => { }); }); +describe('sdk-extension validator tests', () => { + it('Empty sdk-extension is acceptable, means default', () => { + const func = () => { + validator.checkSDKExtension(''); + }; + expect(func).not.toThrow(); + }); + + it('Throws if sdk-extension is not a number', () => { + const func = () => { + validator.checkSDKExtension('-ext9'); + }; + expect(func).toThrowError(`Unexpected SDKExtension: '-ext9'.`); + }); + + it('Throws if sdk-extension is not an integer', () => { + const func = () => { + validator.checkSDKExtension('9.1'); + }; + expect(func).toThrowError(`Unexpected SDKExtension: '9.1'.`); + }); + + it('Validates successfully with valid sdk-extension', () => { + const func1 = () => { + validator.checkSDKExtension('1'); + }; + expect(func1).not.toThrow(); + }); +}); + describe('target validator tests', () => { it('Throws if target is unknown', () => { const func = () => { diff --git a/action-types.yml b/action-types.yml index ecdc5ee5d..0c00f192d 100644 --- a/action-types.yml +++ b/action-types.yml @@ -1,6 +1,8 @@ inputs: api-level: type: integer + sdk-extension: + type: integer target: type: enum allowed-values: diff --git a/action.yml b/action.yml index 663797023..d618bb340 100644 --- a/action.yml +++ b/action.yml @@ -8,6 +8,9 @@ inputs: api-level: description: 'API level of the platform and system image - e.g. 23 for Android Marshmallow, 29 for Android 10' required: true + sdk-extension: + description: 'SDK extension of a given api level - e.g. 9 for Android API 34 it will translate to 34-ext9. Check https://developer.android.com/guide/sdk-extensions for more details. `-ext` should not be part of the input, the input should be an integer.' + required: false 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, andrdoid-automotive, android-automotive-playstore or android-desktop' default: 'default' diff --git a/lib/emulator-manager.js b/lib/emulator-manager.js index a74991e14..8b53d102f 100644 --- a/lib/emulator-manager.js +++ b/lib/emulator-manager.js @@ -38,17 +38,18 @@ const fs = __importStar(require("fs")); /** * Creates and launches a new AVD instance with the specified configurations. */ -function launchEmulator(apiLevel, target, arch, profile, cores, ramSize, heapSize, sdcardPathOrSize, diskSize, avdName, forceAvdCreation, emulatorBootTimeout, port, emulatorOptions, disableAnimations, disableSpellChecker, disableLinuxHardwareAcceleration, enableHardwareKeyboard) { +function launchEmulator(apiLevel, sdkExtension, target, arch, profile, cores, ramSize, heapSize, sdcardPathOrSize, diskSize, avdName, forceAvdCreation, emulatorBootTimeout, port, emulatorOptions, disableAnimations, disableSpellChecker, disableLinuxHardwareAcceleration, enableHardwareKeyboard) { return __awaiter(this, void 0, void 0, function* () { try { console.log(`::group::Launch Emulator`); + const apiTag = sdkExtension ? `${apiLevel}-ext${sdkExtension}` : `${apiLevel}`; // 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.`); - yield exec.exec(`sh -c \\"echo no | avdmanager create avd --force -n "${avdName}" --abi '${target}/${arch}' --package 'system-images;android-${apiLevel};${target};${arch}' ${profileOption} ${sdcardPathOrSizeOption}"`); + yield exec.exec(`sh -c \\"echo no | avdmanager create avd --force -n "${avdName}" --abi '${target}/${arch}' --package 'system-images;android-${apiTag};${target};${arch}' ${profileOption} ${sdcardPathOrSizeOption}"`); } if (cores) { yield exec.exec(`sh -c \\"printf 'hw.cpu.ncore=${cores}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`); diff --git a/lib/input-validator.js b/lib/input-validator.js index 6ba796361..441e16205 100644 --- a/lib/input-validator.js +++ b/lib/input-validator.js @@ -1,8 +1,21 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.checkDiskSize = exports.checkEmulatorBuild = exports.checkEnableHardwareKeyboard = exports.checkDisableLinuxHardwareAcceleration = exports.checkDisableSpellchecker = exports.checkDisableAnimations = exports.checkPort = exports.checkForceAvdCreation = exports.checkChannel = exports.checkArch = exports.checkTarget = exports.checkApiLevel = exports.PREVIEW_API_LEVELS = exports.MAX_PORT = exports.MIN_PORT = exports.VALID_CHANNELS = exports.VALID_ARCHS = exports.VALID_TARGETS = exports.MIN_API_LEVEL = void 0; +exports.checkDiskSize = exports.checkEmulatorBuild = exports.checkEnableHardwareKeyboard = exports.checkDisableLinuxHardwareAcceleration = exports.checkDisableSpellchecker = exports.checkDisableAnimations = exports.checkPort = exports.checkForceAvdCreation = exports.checkChannel = exports.checkArch = exports.checkTarget = exports.checkSDKExtension = exports.checkApiLevel = exports.PREVIEW_API_LEVELS = exports.MAX_PORT = exports.MIN_PORT = exports.VALID_CHANNELS = exports.VALID_ARCHS = exports.VALID_TARGETS = exports.MIN_API_LEVEL = void 0; exports.MIN_API_LEVEL = 15; -exports.VALID_TARGETS = ['default', 'google_apis', 'aosp_atd', 'google_atd', 'google_apis_playstore', 'android-wear', 'android-wear-cn', 'android-tv', 'google-tv', 'andrdoid-automotive', 'android-automotive-playstore', 'android-desktop']; +exports.VALID_TARGETS = [ + 'default', + 'google_apis', + 'aosp_atd', + 'google_atd', + 'google_apis_playstore', + 'android-wear', + 'android-wear-cn', + 'android-tv', + 'google-tv', + 'andrdoid-automotive', + 'android-automotive-playstore', + 'android-desktop' +]; exports.VALID_ARCHS = ['x86', 'x86_64', 'arm64-v8a']; exports.VALID_CHANNELS = ['stable', 'beta', 'dev', 'canary']; exports.MIN_PORT = 5554; @@ -19,6 +32,15 @@ function checkApiLevel(apiLevel) { } } exports.checkApiLevel = checkApiLevel; +function checkSDKExtension(sdkExtension) { + // SDK extension can be empty - the default value + if (sdkExtension) { + if (isNaN(Number(sdkExtension)) || !Number.isInteger(Number(sdkExtension))) { + throw new Error(`Unexpected SDKExtension: '${sdkExtension}'.`); + } + } +} +exports.checkSDKExtension = checkSDKExtension; function checkTarget(target) { if (!exports.VALID_TARGETS.includes(target)) { throw new Error(`Value for input.target '${target}' is unknown. Supported options: ${exports.VALID_TARGETS}.`); diff --git a/lib/main.js b/lib/main.js index 21a5d4918..7d39e723c 100644 --- a/lib/main.js +++ b/lib/main.js @@ -65,6 +65,12 @@ function run() { const apiLevel = core.getInput('api-level', { required: true }); (0, input_validator_1.checkApiLevel)(apiLevel); console.log(`API level: ${apiLevel}`); + // SDK extension + const sdkExtension = core.getInput('sdk-extension'); + (0, input_validator_1.checkSDKExtension)(sdkExtension); + if (sdkExtension) { + console.log(`SDK extension: ${sdkExtension}`); + } // target of the system image const targetInput = core.getInput('target'); const target = targetInput == 'playstore' ? 'google_apis_playstore' : targetInput; @@ -179,7 +185,7 @@ function run() { })); console.log(`::endgroup::`); // install SDK - yield (0, sdk_installer_1.installAndroidSdk)(apiLevel, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion); + yield (0, sdk_installer_1.installAndroidSdk)(apiLevel, sdkExtension, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion); // execute pre emulator launch script if set if (preEmulatorLaunchScripts !== undefined) { console.log(`::group::Run pre emulator launch script`); @@ -198,7 +204,7 @@ function run() { console.log(`::endgroup::`); } // launch an emulator - yield (0, emulator_manager_1.launchEmulator)(apiLevel, target, arch, profile, cores, ramSize, heapSize, sdcardPathOrSize, diskSize, avdName, forceAvdCreation, emulatorBootTimeout, port, emulatorOptions, disableAnimations, disableSpellchecker, disableLinuxHardwareAcceleration, enableHardwareKeyboard); + yield (0, emulator_manager_1.launchEmulator)(apiLevel, sdkExtension, target, arch, profile, cores, ramSize, heapSize, sdcardPathOrSize, diskSize, avdName, forceAvdCreation, emulatorBootTimeout, port, emulatorOptions, disableAnimations, disableSpellchecker, disableLinuxHardwareAcceleration, enableHardwareKeyboard); // execute the custom script try { // move to custom working directory if set diff --git a/lib/sdk-installer.js b/lib/sdk-installer.js index fe7f51385..e0d98bd25 100644 --- a/lib/sdk-installer.js +++ b/lib/sdk-installer.js @@ -46,12 +46,13 @@ const CMDLINE_TOOLS_URL_LINUX = 'https://dl.google.com/android/repository/comman * 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, channelId, emulatorBuild, ndkVersion, cmakeVersion) { +function installAndroidSdk(apiLevel, sdkExtension, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion) { return __awaiter(this, void 0, void 0, function* () { try { console.log(`::group::Install Android SDK`); const isOnMac = process.platform === 'darwin'; const isArm = process.arch === 'arm64'; + const apiTag = sdkExtension ? `${apiLevel}-ext${sdkExtension}` : `${apiLevel}`; const cmdlineToolsPath = `${process.env.ANDROID_HOME}/cmdline-tools`; if (!fs.existsSync(cmdlineToolsPath)) { console.log('Installing new cmdline-tools.'); @@ -68,7 +69,7 @@ function installAndroidSdk(apiLevel, target, arch, channelId, emulatorBuild, ndk // accept all Android SDK licenses yield exec.exec(`sh -c \\"yes | sdkmanager --licenses > /dev/null"`); console.log('Installing latest build tools, platform tools, and platform.'); - yield exec.exec(`sh -c \\"sdkmanager --install 'build-tools;${BUILD_TOOLS_VERSION}' platform-tools 'platforms;android-${apiLevel}'> /dev/null"`); + yield exec.exec(`sh -c \\"sdkmanager --install 'build-tools;${BUILD_TOOLS_VERSION}' platform-tools 'platforms;android-${apiTag}'> /dev/null"`); console.log('Installing latest emulator.'); yield exec.exec(`sh -c \\"sdkmanager --install emulator --channel=${channelId} > /dev/null"`); if (emulatorBuild) { @@ -95,7 +96,7 @@ function installAndroidSdk(apiLevel, target, arch, channelId, emulatorBuild, ndk yield io.rmRF('emulator.zip'); } console.log('Installing system images.'); - yield exec.exec(`sh -c \\"sdkmanager --install 'system-images;android-${apiLevel};${target};${arch}' --channel=${channelId} > /dev/null"`); + yield exec.exec(`sh -c \\"sdkmanager --install 'system-images;android-${apiTag};${target};${arch}' --channel=${channelId} > /dev/null"`); if (ndkVersion) { console.log(`Installing NDK ${ndkVersion}.`); yield exec.exec(`sh -c \\"sdkmanager --install 'ndk;${ndkVersion}' --channel=${channelId} > /dev/null"`); diff --git a/src/emulator-manager.ts b/src/emulator-manager.ts index aa2a67b6d..d5185cb11 100644 --- a/src/emulator-manager.ts +++ b/src/emulator-manager.ts @@ -6,6 +6,7 @@ import * as fs from 'fs'; */ export async function launchEmulator( apiLevel: string, + sdkExtension: string, target: string, arch: string, profile: string, @@ -26,6 +27,7 @@ export async function launchEmulator( ): Promise { try { console.log(`::group::Launch Emulator`); + const apiTag = sdkExtension ? `${apiLevel}-ext${sdkExtension}` : `${apiLevel}`; // 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) { @@ -33,7 +35,7 @@ export async function launchEmulator( const sdcardPathOrSizeOption = sdcardPathOrSize.trim() !== '' ? `--sdcard '${sdcardPathOrSize}'` : ''; console.log(`Creating AVD.`); await exec.exec( - `sh -c \\"echo no | avdmanager create avd --force -n "${avdName}" --abi '${target}/${arch}' --package 'system-images;android-${apiLevel};${target};${arch}' ${profileOption} ${sdcardPathOrSizeOption}"` + `sh -c \\"echo no | avdmanager create avd --force -n "${avdName}" --abi '${target}/${arch}' --package 'system-images;android-${apiTag};${target};${arch}' ${profileOption} ${sdcardPathOrSizeOption}"` ); } diff --git a/src/input-validator.ts b/src/input-validator.ts index f4f70475b..93ca44f00 100644 --- a/src/input-validator.ts +++ b/src/input-validator.ts @@ -11,7 +11,8 @@ export const VALID_TARGETS: Array = [ 'google-tv', 'andrdoid-automotive', 'android-automotive-playstore', - 'android-desktop']; + 'android-desktop', +]; export const VALID_ARCHS: Array = ['x86', 'x86_64', 'arm64-v8a']; export const VALID_CHANNELS: Array = ['stable', 'beta', 'dev', 'canary']; export const MIN_PORT = 5554; @@ -28,6 +29,15 @@ export function checkApiLevel(apiLevel: string): void { } } +export function checkSDKExtension(sdkExtension: string): void { + // SDK extension can be empty - the default value + if (sdkExtension) { + if (isNaN(Number(sdkExtension)) || !Number.isInteger(Number(sdkExtension))) { + throw new Error(`Unexpected SDKExtension: '${sdkExtension}'.`); + } + } +} + 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}.`); diff --git a/src/main.ts b/src/main.ts index a9588245e..67fd25561 100644 --- a/src/main.ts +++ b/src/main.ts @@ -14,6 +14,7 @@ import { checkDiskSize, checkPort, MIN_PORT, + checkSDKExtension, } from './input-validator'; import { launchEmulator, killEmulator } from './emulator-manager'; import * as exec from '@actions/exec'; @@ -47,6 +48,13 @@ async function run() { checkApiLevel(apiLevel); console.log(`API level: ${apiLevel}`); + // SDK extension + const sdkExtension = core.getInput('sdk-extension'); + checkSDKExtension(sdkExtension); + if (sdkExtension) { + console.log(`SDK extension: ${sdkExtension}`); + } + // target of the system image const targetInput = core.getInput('target'); const target = targetInput == 'playstore' ? 'google_apis_playstore' : targetInput; @@ -185,7 +193,7 @@ async function run() { console.log(`::endgroup::`); // install SDK - await installAndroidSdk(apiLevel, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion); + await installAndroidSdk(apiLevel, sdkExtension, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion); // execute pre emulator launch script if set if (preEmulatorLaunchScripts !== undefined) { @@ -207,6 +215,7 @@ async function run() { // launch an emulator await launchEmulator( apiLevel, + sdkExtension, target, arch, profile, diff --git a/src/sdk-installer.ts b/src/sdk-installer.ts index f87028a6f..8a235e38b 100644 --- a/src/sdk-installer.ts +++ b/src/sdk-installer.ts @@ -13,11 +13,21 @@ const CMDLINE_TOOLS_URL_LINUX = 'https://dl.google.com/android/repository/comman * 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: string, target: string, arch: string, channelId: number, emulatorBuild?: string, ndkVersion?: string, cmakeVersion?: string): Promise { +export async function installAndroidSdk( + apiLevel: string, + sdkExtension: String, + target: string, + arch: string, + channelId: number, + emulatorBuild?: string, + ndkVersion?: string, + cmakeVersion?: string +): Promise { try { console.log(`::group::Install Android SDK`); const isOnMac = process.platform === 'darwin'; const isArm = process.arch === 'arm64'; + const apiTag = sdkExtension ? `${apiLevel}-ext${sdkExtension}` : `${apiLevel}`; const cmdlineToolsPath = `${process.env.ANDROID_HOME}/cmdline-tools`; if (!fs.existsSync(cmdlineToolsPath)) { @@ -40,7 +50,7 @@ export async function installAndroidSdk(apiLevel: string, target: string, arch: 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-${apiTag}'> /dev/null"`); console.log('Installing latest emulator.'); await exec.exec(`sh -c \\"sdkmanager --install emulator --channel=${channelId} > /dev/null"`); @@ -66,7 +76,7 @@ export async function installAndroidSdk(apiLevel: string, target: string, arch: await io.rmRF('emulator.zip'); } console.log('Installing system images.'); - await exec.exec(`sh -c \\"sdkmanager --install 'system-images;android-${apiLevel};${target};${arch}' --channel=${channelId} > /dev/null"`); + await exec.exec(`sh -c \\"sdkmanager --install 'system-images;android-${apiTag};${target};${arch}' --channel=${channelId} > /dev/null"`); if (ndkVersion) { console.log(`Installing NDK ${ndkVersion}.`); From b5ec074e920c41c7fd72b9704e82a9b78ffd0177 Mon Sep 17 00:00:00 2001 From: Timothy <6560631+TimoPtr@users.noreply.github.com> Date: Tue, 18 Mar 2025 15:37:39 +0100 Subject: [PATCH 4/7] Fix typo and copy paste --- .github/workflows/manually.yml | 2 +- README.md | 2 +- __tests__/input-validator.test.ts | 2 +- action-types.yml | 2 +- action.yml | 2 +- lib/input-validator.js | 2 +- src/input-validator.ts | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/manually.yml b/.github/workflows/manually.yml index 58140272e..04634951f 100644 --- a/.github/workflows/manually.yml +++ b/.github/workflows/manually.yml @@ -13,7 +13,7 @@ on: sdk-extension: description: 'SDK extension of a given api level' 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, andrdoid-automotive, android-automotive-playstore or android-desktop' + 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: diff --git a/README.md b/README.md index db31f1441..1c0636f83 100644 --- a/README.md +++ b/README.md @@ -206,7 +206,7 @@ jobs: |-|-|-|-| | `api-level` | Required | N/A | API level of the platform system image - e.g. 23 for Android Marshmallow, 29 for Android 10. **Minimum API level supported is 15**. | | `sdk-extension` | Optional | N/A | SDK extension of a given api level - e.g. 9 for Android API 34 it will translate to 34-ext9. Check https://developer.android.com/guide/sdk-extensions for more details. `-ext` should not be part of the input, **the input should be an integer**. | -| `target` | Optional | `default` | Target of the system image - `default`, `google_apis`, `playstore`, `android-wear`, `android-wear-cn`, `android-tv`, `google-tv`, `aosp_atd`, `google_atd`, `andrdoid-automotive`, `android-automotive-playstore` or `android-desktop`. Note that `aosp_atd` and `google_atd` currently require the following: `api-level: 30`, `arch: x86` or `arch: arm64-v8` and `channel: canary`. | +| `target` | Optional | `default` | Target of the system image - `default`, `google_apis`, `playstore`, `android-wear`, `android-wear-cn`, `android-tv`, `google-tv`, `aosp_atd`, `google_atd`, `android-automotive`, `android-automotive-playstore` or `android-desktop`. Note that `aosp_atd` and `google_atd` currently require the following: `api-level: 30`, `arch: x86` or `arch: arm64-v8` and `channel: canary`. | | `arch` | Optional | `x86` | CPU architecture of the system image - `x86`, `x86_64` or `arm64-v8a`. Note that `x86_64` image is only available for API 21+. `arm64-v8a` images require Android 4.2+ and are limited to fewer API levels (e.g. 30). | | `profile` | Optional | N/A | Hardware profile used for creating the AVD - e.g. `Nexus 6`. For a list of all profiles available, run `avdmanager list device`. | | `cores` | Optional | 2 | Number of cores to use for the emulator (`hw.cpu.ncore` in config.ini). | diff --git a/__tests__/input-validator.test.ts b/__tests__/input-validator.test.ts index 0f7be5d24..25ede8b86 100644 --- a/__tests__/input-validator.test.ts +++ b/__tests__/input-validator.test.ts @@ -129,7 +129,7 @@ describe('target validator tests', () => { expect(func9).not.toThrow(); const func10 = () => { - validator.checkTarget('andrdoid-automotive'); + validator.checkTarget('android-automotive'); }; expect(func10).not.toThrow(); diff --git a/action-types.yml b/action-types.yml index 0c00f192d..32fff3032 100644 --- a/action-types.yml +++ b/action-types.yml @@ -15,7 +15,7 @@ inputs: - android-wear-cn - android-tv - google-tv - - andrdoid-automotive + - android-automotive - android-automotive-playstore - android-desktop arch: diff --git a/action.yml b/action.yml index d618bb340..b109e6813 100644 --- a/action.yml +++ b/action.yml @@ -12,7 +12,7 @@ inputs: description: 'SDK extension of a given api level - e.g. 9 for Android API 34 it will translate to 34-ext9. Check https://developer.android.com/guide/sdk-extensions for more details. `-ext` should not be part of the input, the input should be an integer.' required: false 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, andrdoid-automotive, android-automotive-playstore or android-desktop' + 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' default: 'default' arch: description: 'CPU architecture of the system image - x86, x86_64 or arm64-v8a' diff --git a/lib/input-validator.js b/lib/input-validator.js index 441e16205..60a3f15da 100644 --- a/lib/input-validator.js +++ b/lib/input-validator.js @@ -12,7 +12,7 @@ exports.VALID_TARGETS = [ 'android-wear-cn', 'android-tv', 'google-tv', - 'andrdoid-automotive', + 'android-automotive', 'android-automotive-playstore', 'android-desktop' ]; diff --git a/src/input-validator.ts b/src/input-validator.ts index 93ca44f00..8ca47b201 100644 --- a/src/input-validator.ts +++ b/src/input-validator.ts @@ -9,7 +9,7 @@ export const VALID_TARGETS: Array = [ 'android-wear-cn', 'android-tv', 'google-tv', - 'andrdoid-automotive', + 'android-automotive', 'android-automotive-playstore', 'android-desktop', ]; From 1f68daf0e87f8d8f0fb5231a83cc49477697a065 Mon Sep 17 00:00:00 2001 From: Timothy <6560631+TimoPtr@users.noreply.github.com> Date: Mon, 24 Mar 2025 14:08:46 +0100 Subject: [PATCH 5/7] Stop checking api-level and introduce system-image-api-level --- .github/workflows/main.yml | 8 ++-- .github/workflows/manually.yml | 8 ++-- README.md | 6 +-- __tests__/input-validator.test.ts | 73 ------------------------------- action-types.yml | 6 +-- action.yml | 4 +- lib/emulator-manager.js | 5 +-- lib/input-validator.js | 16 +------ lib/main.js | 14 +++--- lib/sdk-installer.js | 7 ++- src/emulator-manager.ts | 6 +-- src/input-validator.ts | 11 ----- src/main.ts | 16 +++---- src/sdk-installer.ts | 7 ++- 14 files changed, 40 insertions(+), 147 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c6720a137..e8cd13b3b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -40,10 +40,10 @@ jobs: target: google_apis arch: x86_64 - os: ubuntu-latest - api-level: 34 + api-level: 34-ext10 target: android-automotive arch: x86_64 - sdk-extension: 9 + system-image-api-level: 34-ext9 steps: - name: checkout @@ -90,7 +90,7 @@ jobs: api-level: ${{ matrix.api-level }} target: ${{ matrix.target }} arch: ${{ matrix.arch }} - sdk-extension: ${{ matrix.sdk-extension }} + system-image-api-level: ${{ matrix.system-image-api-level }} profile: Galaxy Nexus cores: 2 sdcard-path-or-size: 100M @@ -108,7 +108,7 @@ jobs: api-level: ${{ matrix.api-level }} target: ${{ matrix.target }} arch: ${{ matrix.arch }} - sdk-extension: ${{ matrix.sdk-extension }} + system-image-api-level: ${{ matrix.system-image-api-level }} profile: Galaxy Nexus cores: 2 ram-size: 2048M diff --git a/.github/workflows/manually.yml b/.github/workflows/manually.yml index 04634951f..eb70fdb70 100644 --- a/.github/workflows/manually.yml +++ b/.github/workflows/manually.yml @@ -7,11 +7,11 @@ on: required: true default: 'ubuntu-latest' api-level: - description: 'API level of the platform and system image' + description: 'API level of the platform and system image (if not overridden with system-image-api-level input)' required: true default: '34' - sdk-extension: - description: 'SDK extension of a given api level' + system-image-api-level: + description: 'API level of the system image' 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 @@ -70,7 +70,7 @@ jobs: api-level: ${{ github.event.inputs.api-level }} target: ${{ github.event.inputs.target }} arch: ${{ github.event.inputs.arch }} - sdk-extension: ${{ github.event.inputs.sdk-extension }} + 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 }} diff --git a/README.md b/README.md index 1c0636f83..4f68e1bbf 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,7 @@ jobs: script: ./gradlew connectedCheck ``` -If you need a specific [SDKExtension](https://developer.android.com/guide/sdk-extensions) +If you need a specific [SDKExtension](https://developer.android.com/guide/sdk-extensions) for the system image but not the platform ```yml jobs: @@ -139,7 +139,7 @@ jobs: uses: reactivecircus/android-emulator-runner@v2 with: api-level: 34 - sdk-extension: 9 + system-image-api-level: 34-ext9 target: android-automotive script: ./gradlew connectedCheck ``` @@ -205,7 +205,7 @@ jobs: | **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**. | -| `sdk-extension` | Optional | N/A | SDK extension of a given api level - e.g. 9 for Android API 34 it will translate to 34-ext9. Check https://developer.android.com/guide/sdk-extensions for more details. `-ext` should not be part of the input, **the input should be an integer**. | +| `system-image-api-level` | Optional | `ap-level` | API level of the system image - e.g. 23 for Android Marshmallow, 29 for Android 10. | | `target` | Optional | `default` | Target of the system image - `default`, `google_apis`, `playstore`, `android-wear`, `android-wear-cn`, `android-tv`, `google-tv`, `aosp_atd`, `google_atd`, `android-automotive`, `android-automotive-playstore` or `android-desktop`. Note that `aosp_atd` and `google_atd` currently require the following: `api-level: 30`, `arch: x86` or `arch: arm64-v8` and `channel: canary`. | | `arch` | Optional | `x86` | CPU architecture of the system image - `x86`, `x86_64` or `arm64-v8a`. Note that `x86_64` image is only available for API 21+. `arm64-v8a` images require Android 4.2+ and are limited to fewer API levels (e.g. 30). | | `profile` | Optional | N/A | Hardware profile used for creating the AVD - e.g. `Nexus 6`. For a list of all profiles available, run `avdmanager list device`. | diff --git a/__tests__/input-validator.test.ts b/__tests__/input-validator.test.ts index 25ede8b86..91c409039 100644 --- a/__tests__/input-validator.test.ts +++ b/__tests__/input-validator.test.ts @@ -1,79 +1,6 @@ import * as validator from '../src/input-validator'; import { MAX_PORT, MIN_PORT } 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(); - const func3 = () => { - validator.checkApiLevel('UpsideDownCake-ext5'); - }; - expect(func3).not.toThrow(); - const func4 = () => { - validator.checkApiLevel('TiramisuPrivacySandbox'); - }; - expect(func4).not.toThrow(); - }); -}); - -describe('sdk-extension validator tests', () => { - it('Empty sdk-extension is acceptable, means default', () => { - const func = () => { - validator.checkSDKExtension(''); - }; - expect(func).not.toThrow(); - }); - - it('Throws if sdk-extension is not a number', () => { - const func = () => { - validator.checkSDKExtension('-ext9'); - }; - expect(func).toThrowError(`Unexpected SDKExtension: '-ext9'.`); - }); - - it('Throws if sdk-extension is not an integer', () => { - const func = () => { - validator.checkSDKExtension('9.1'); - }; - expect(func).toThrowError(`Unexpected SDKExtension: '9.1'.`); - }); - - it('Validates successfully with valid sdk-extension', () => { - const func1 = () => { - validator.checkSDKExtension('1'); - }; - expect(func1).not.toThrow(); - }); -}); - describe('target validator tests', () => { it('Throws if target is unknown', () => { const func = () => { diff --git a/action-types.yml b/action-types.yml index 32fff3032..49bcc51be 100644 --- a/action-types.yml +++ b/action-types.yml @@ -1,8 +1,8 @@ inputs: api-level: - type: integer - sdk-extension: - type: integer + type: string + system-image-api-level: + type: string target: type: enum allowed-values: diff --git a/action.yml b/action.yml index b109e6813..7a6e370cb 100644 --- a/action.yml +++ b/action.yml @@ -8,8 +8,8 @@ inputs: api-level: description: 'API level of the platform and system image - e.g. 23 for Android Marshmallow, 29 for Android 10' required: true - sdk-extension: - description: 'SDK extension of a given api level - e.g. 9 for Android API 34 it will translate to 34-ext9. Check https://developer.android.com/guide/sdk-extensions for more details. `-ext` should not be part of the input, the input should be an integer.' + system-image-api-level: + description: 'API level of the system image - e.g. 23 for Android Marshmallow, 29 for Android 10. If not set the `api-level` input will be used.' required: false 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' diff --git a/lib/emulator-manager.js b/lib/emulator-manager.js index 8b53d102f..56a6d119b 100644 --- a/lib/emulator-manager.js +++ b/lib/emulator-manager.js @@ -38,18 +38,17 @@ const fs = __importStar(require("fs")); /** * Creates and launches a new AVD instance with the specified configurations. */ -function launchEmulator(apiLevel, sdkExtension, target, arch, profile, cores, ramSize, heapSize, sdcardPathOrSize, diskSize, avdName, forceAvdCreation, emulatorBootTimeout, port, emulatorOptions, disableAnimations, disableSpellChecker, disableLinuxHardwareAcceleration, enableHardwareKeyboard) { +function launchEmulator(systemImageApiLevel, target, arch, profile, cores, ramSize, heapSize, sdcardPathOrSize, diskSize, avdName, forceAvdCreation, emulatorBootTimeout, port, emulatorOptions, disableAnimations, disableSpellChecker, disableLinuxHardwareAcceleration, enableHardwareKeyboard) { return __awaiter(this, void 0, void 0, function* () { try { console.log(`::group::Launch Emulator`); - const apiTag = sdkExtension ? `${apiLevel}-ext${sdkExtension}` : `${apiLevel}`; // 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.`); - yield exec.exec(`sh -c \\"echo no | avdmanager create avd --force -n "${avdName}" --abi '${target}/${arch}' --package 'system-images;android-${apiTag};${target};${arch}' ${profileOption} ${sdcardPathOrSizeOption}"`); + yield exec.exec(`sh -c \\"echo no | avdmanager create avd --force -n "${avdName}" --abi '${target}/${arch}' --package 'system-images;android-${systemImageApiLevel};${target};${arch}' ${profileOption} ${sdcardPathOrSizeOption}"`); } if (cores) { yield exec.exec(`sh -c \\"printf 'hw.cpu.ncore=${cores}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`); diff --git a/lib/input-validator.js b/lib/input-validator.js index 60a3f15da..b50be5c30 100644 --- a/lib/input-validator.js +++ b/lib/input-validator.js @@ -1,6 +1,6 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.checkDiskSize = exports.checkEmulatorBuild = exports.checkEnableHardwareKeyboard = exports.checkDisableLinuxHardwareAcceleration = exports.checkDisableSpellchecker = exports.checkDisableAnimations = exports.checkPort = exports.checkForceAvdCreation = exports.checkChannel = exports.checkArch = exports.checkTarget = exports.checkSDKExtension = exports.checkApiLevel = exports.PREVIEW_API_LEVELS = exports.MAX_PORT = exports.MIN_PORT = exports.VALID_CHANNELS = exports.VALID_ARCHS = exports.VALID_TARGETS = exports.MIN_API_LEVEL = void 0; +exports.checkDiskSize = exports.checkEmulatorBuild = exports.checkEnableHardwareKeyboard = exports.checkDisableLinuxHardwareAcceleration = exports.checkDisableSpellchecker = exports.checkDisableAnimations = exports.checkPort = exports.checkForceAvdCreation = exports.checkChannel = exports.checkArch = exports.checkTarget = exports.checkSDKExtension = exports.MAX_PORT = exports.MIN_PORT = exports.VALID_CHANNELS = exports.VALID_ARCHS = exports.VALID_TARGETS = exports.MIN_API_LEVEL = void 0; exports.MIN_API_LEVEL = 15; exports.VALID_TARGETS = [ 'default', @@ -14,24 +14,12 @@ exports.VALID_TARGETS = [ 'google-tv', 'android-automotive', 'android-automotive-playstore', - 'android-desktop' + 'android-desktop', ]; exports.VALID_ARCHS = ['x86', 'x86_64', 'arm64-v8a']; exports.VALID_CHANNELS = ['stable', 'beta', 'dev', 'canary']; exports.MIN_PORT = 5554; exports.MAX_PORT = 5584; -exports.PREVIEW_API_LEVELS = ['Tiramisu', 'UpsideDownCake', 'VanillaIceCream', 'Baklava']; -function checkApiLevel(apiLevel) { - if (exports.PREVIEW_API_LEVELS.some((previewLevel) => apiLevel.startsWith(previewLevel))) - return; - 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.checkApiLevel = checkApiLevel; function checkSDKExtension(sdkExtension) { // SDK extension can be empty - the default value if (sdkExtension) { diff --git a/lib/main.js b/lib/main.js index 7d39e723c..9bff6cbd7 100644 --- a/lib/main.js +++ b/lib/main.js @@ -63,14 +63,12 @@ function run() { } // API level of the platform and system image const apiLevel = core.getInput('api-level', { required: true }); - (0, input_validator_1.checkApiLevel)(apiLevel); console.log(`API level: ${apiLevel}`); - // SDK extension - const sdkExtension = core.getInput('sdk-extension'); - (0, input_validator_1.checkSDKExtension)(sdkExtension); - if (sdkExtension) { - console.log(`SDK extension: ${sdkExtension}`); + 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; @@ -185,7 +183,7 @@ function run() { })); console.log(`::endgroup::`); // install SDK - yield (0, sdk_installer_1.installAndroidSdk)(apiLevel, sdkExtension, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion); + yield (0, sdk_installer_1.installAndroidSdk)(apiLevel, systemImageApiLevel, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion); // execute pre emulator launch script if set if (preEmulatorLaunchScripts !== undefined) { console.log(`::group::Run pre emulator launch script`); @@ -204,7 +202,7 @@ function run() { console.log(`::endgroup::`); } // launch an emulator - yield (0, emulator_manager_1.launchEmulator)(apiLevel, sdkExtension, target, arch, profile, cores, ramSize, heapSize, sdcardPathOrSize, diskSize, avdName, forceAvdCreation, emulatorBootTimeout, port, emulatorOptions, disableAnimations, disableSpellchecker, disableLinuxHardwareAcceleration, enableHardwareKeyboard); + yield (0, emulator_manager_1.launchEmulator)(systemImageApiLevel, target, arch, profile, cores, ramSize, heapSize, sdcardPathOrSize, diskSize, avdName, forceAvdCreation, emulatorBootTimeout, port, emulatorOptions, disableAnimations, disableSpellchecker, disableLinuxHardwareAcceleration, enableHardwareKeyboard); // execute the custom script try { // move to custom working directory if set diff --git a/lib/sdk-installer.js b/lib/sdk-installer.js index e0d98bd25..237b67f59 100644 --- a/lib/sdk-installer.js +++ b/lib/sdk-installer.js @@ -46,13 +46,12 @@ const CMDLINE_TOOLS_URL_LINUX = 'https://dl.google.com/android/repository/comman * 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, sdkExtension, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion) { +function installAndroidSdk(apiLevel, systemImageApiLevel, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion) { return __awaiter(this, void 0, void 0, function* () { try { console.log(`::group::Install Android SDK`); const isOnMac = process.platform === 'darwin'; const isArm = process.arch === 'arm64'; - const apiTag = sdkExtension ? `${apiLevel}-ext${sdkExtension}` : `${apiLevel}`; const cmdlineToolsPath = `${process.env.ANDROID_HOME}/cmdline-tools`; if (!fs.existsSync(cmdlineToolsPath)) { console.log('Installing new cmdline-tools.'); @@ -69,7 +68,7 @@ function installAndroidSdk(apiLevel, sdkExtension, target, arch, channelId, emul // accept all Android SDK licenses yield exec.exec(`sh -c \\"yes | sdkmanager --licenses > /dev/null"`); console.log('Installing latest build tools, platform tools, and platform.'); - yield exec.exec(`sh -c \\"sdkmanager --install 'build-tools;${BUILD_TOOLS_VERSION}' platform-tools 'platforms;android-${apiTag}'> /dev/null"`); + yield exec.exec(`sh -c \\"sdkmanager --install 'build-tools;${BUILD_TOOLS_VERSION}' platform-tools 'platforms;android-${apiLevel}'> /dev/null"`); console.log('Installing latest emulator.'); yield exec.exec(`sh -c \\"sdkmanager --install emulator --channel=${channelId} > /dev/null"`); if (emulatorBuild) { @@ -96,7 +95,7 @@ function installAndroidSdk(apiLevel, sdkExtension, target, arch, channelId, emul yield io.rmRF('emulator.zip'); } console.log('Installing system images.'); - yield exec.exec(`sh -c \\"sdkmanager --install 'system-images;android-${apiTag};${target};${arch}' --channel=${channelId} > /dev/null"`); + yield 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}' --channel=${channelId} > /dev/null"`); diff --git a/src/emulator-manager.ts b/src/emulator-manager.ts index d5185cb11..14684dede 100644 --- a/src/emulator-manager.ts +++ b/src/emulator-manager.ts @@ -5,8 +5,7 @@ import * as fs from 'fs'; * Creates and launches a new AVD instance with the specified configurations. */ export async function launchEmulator( - apiLevel: string, - sdkExtension: string, + systemImageApiLevel: string, target: string, arch: string, profile: string, @@ -27,7 +26,6 @@ export async function launchEmulator( ): Promise { try { console.log(`::group::Launch Emulator`); - const apiTag = sdkExtension ? `${apiLevel}-ext${sdkExtension}` : `${apiLevel}`; // 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) { @@ -35,7 +33,7 @@ export async function launchEmulator( const sdcardPathOrSizeOption = sdcardPathOrSize.trim() !== '' ? `--sdcard '${sdcardPathOrSize}'` : ''; console.log(`Creating AVD.`); await exec.exec( - `sh -c \\"echo no | avdmanager create avd --force -n "${avdName}" --abi '${target}/${arch}' --package 'system-images;android-${apiTag};${target};${arch}' ${profileOption} ${sdcardPathOrSizeOption}"` + `sh -c \\"echo no | avdmanager create avd --force -n "${avdName}" --abi '${target}/${arch}' --package 'system-images;android-${systemImageApiLevel};${target};${arch}' ${profileOption} ${sdcardPathOrSizeOption}"` ); } diff --git a/src/input-validator.ts b/src/input-validator.ts index 8ca47b201..cb7f45e65 100644 --- a/src/input-validator.ts +++ b/src/input-validator.ts @@ -17,17 +17,6 @@ export const VALID_ARCHS: Array = ['x86', 'x86_64', 'arm64-v8a']; export const VALID_CHANNELS: Array = ['stable', 'beta', 'dev', 'canary']; export const MIN_PORT = 5554; export const MAX_PORT = 5584; -export const PREVIEW_API_LEVELS: Array = ['Tiramisu', 'UpsideDownCake', 'VanillaIceCream', 'Baklava']; - -export function checkApiLevel(apiLevel: string): void { - if (PREVIEW_API_LEVELS.some((previewLevel) => apiLevel.startsWith(previewLevel))) return; - 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 checkSDKExtension(sdkExtension: string): void { // SDK extension can be empty - the default value diff --git a/src/main.ts b/src/main.ts index 67fd25561..bec4a08db 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,7 +1,6 @@ import * as core from '@actions/core'; import { installAndroidSdk } from './sdk-installer'; import { - checkApiLevel, checkTarget, checkArch, checkDisableAnimations, @@ -45,15 +44,13 @@ async function run() { // API level of the platform and system image const apiLevel = core.getInput('api-level', { required: true }); - checkApiLevel(apiLevel); console.log(`API level: ${apiLevel}`); - // SDK extension - const sdkExtension = core.getInput('sdk-extension'); - checkSDKExtension(sdkExtension); - if (sdkExtension) { - console.log(`SDK extension: ${sdkExtension}`); + 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'); @@ -193,7 +190,7 @@ async function run() { console.log(`::endgroup::`); // install SDK - await installAndroidSdk(apiLevel, sdkExtension, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion); + await installAndroidSdk(apiLevel, systemImageApiLevel, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion); // execute pre emulator launch script if set if (preEmulatorLaunchScripts !== undefined) { @@ -214,8 +211,7 @@ async function run() { // launch an emulator await launchEmulator( - apiLevel, - sdkExtension, + systemImageApiLevel, target, arch, profile, diff --git a/src/sdk-installer.ts b/src/sdk-installer.ts index 8a235e38b..b2e1baf28 100644 --- a/src/sdk-installer.ts +++ b/src/sdk-installer.ts @@ -15,7 +15,7 @@ const CMDLINE_TOOLS_URL_LINUX = 'https://dl.google.com/android/repository/comman */ export async function installAndroidSdk( apiLevel: string, - sdkExtension: String, + systemImageApiLevel: String, target: string, arch: string, channelId: number, @@ -27,7 +27,6 @@ export async function installAndroidSdk( console.log(`::group::Install Android SDK`); const isOnMac = process.platform === 'darwin'; const isArm = process.arch === 'arm64'; - const apiTag = sdkExtension ? `${apiLevel}-ext${sdkExtension}` : `${apiLevel}`; const cmdlineToolsPath = `${process.env.ANDROID_HOME}/cmdline-tools`; if (!fs.existsSync(cmdlineToolsPath)) { @@ -50,7 +49,7 @@ export async function installAndroidSdk( 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-${apiTag}'> /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"`); @@ -76,7 +75,7 @@ export async function installAndroidSdk( await io.rmRF('emulator.zip'); } console.log('Installing system images.'); - await exec.exec(`sh -c \\"sdkmanager --install 'system-images;android-${apiTag};${target};${arch}' --channel=${channelId} > /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}.`); From 59d81ddf7fd5645979454a8244b16ac2589e74ce Mon Sep 17 00:00:00 2001 From: Timothy <6560631+TimoPtr@users.noreply.github.com> Date: Mon, 24 Mar 2025 14:11:13 +0100 Subject: [PATCH 6/7] Remove leftover --- lib/input-validator.js | 11 +---------- src/input-validator.ts | 9 --------- src/main.ts | 1 - 3 files changed, 1 insertion(+), 20 deletions(-) diff --git a/lib/input-validator.js b/lib/input-validator.js index b50be5c30..9960b46bb 100644 --- a/lib/input-validator.js +++ b/lib/input-validator.js @@ -1,6 +1,6 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.checkDiskSize = exports.checkEmulatorBuild = exports.checkEnableHardwareKeyboard = exports.checkDisableLinuxHardwareAcceleration = exports.checkDisableSpellchecker = exports.checkDisableAnimations = exports.checkPort = exports.checkForceAvdCreation = exports.checkChannel = exports.checkArch = exports.checkTarget = exports.checkSDKExtension = exports.MAX_PORT = exports.MIN_PORT = exports.VALID_CHANNELS = exports.VALID_ARCHS = exports.VALID_TARGETS = exports.MIN_API_LEVEL = void 0; +exports.checkDiskSize = exports.checkEmulatorBuild = exports.checkEnableHardwareKeyboard = exports.checkDisableLinuxHardwareAcceleration = exports.checkDisableSpellchecker = exports.checkDisableAnimations = exports.checkPort = exports.checkForceAvdCreation = exports.checkChannel = exports.checkArch = exports.checkTarget = exports.MAX_PORT = exports.MIN_PORT = exports.VALID_CHANNELS = exports.VALID_ARCHS = exports.VALID_TARGETS = exports.MIN_API_LEVEL = void 0; exports.MIN_API_LEVEL = 15; exports.VALID_TARGETS = [ 'default', @@ -20,15 +20,6 @@ exports.VALID_ARCHS = ['x86', 'x86_64', 'arm64-v8a']; exports.VALID_CHANNELS = ['stable', 'beta', 'dev', 'canary']; exports.MIN_PORT = 5554; exports.MAX_PORT = 5584; -function checkSDKExtension(sdkExtension) { - // SDK extension can be empty - the default value - if (sdkExtension) { - if (isNaN(Number(sdkExtension)) || !Number.isInteger(Number(sdkExtension))) { - throw new Error(`Unexpected SDKExtension: '${sdkExtension}'.`); - } - } -} -exports.checkSDKExtension = checkSDKExtension; function checkTarget(target) { if (!exports.VALID_TARGETS.includes(target)) { throw new Error(`Value for input.target '${target}' is unknown. Supported options: ${exports.VALID_TARGETS}.`); diff --git a/src/input-validator.ts b/src/input-validator.ts index cb7f45e65..e88740543 100644 --- a/src/input-validator.ts +++ b/src/input-validator.ts @@ -18,15 +18,6 @@ export const VALID_CHANNELS: Array = ['stable', 'beta', 'dev', 'canary'] export const MIN_PORT = 5554; export const MAX_PORT = 5584; -export function checkSDKExtension(sdkExtension: string): void { - // SDK extension can be empty - the default value - if (sdkExtension) { - if (isNaN(Number(sdkExtension)) || !Number.isInteger(Number(sdkExtension))) { - throw new Error(`Unexpected SDKExtension: '${sdkExtension}'.`); - } - } -} - 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}.`); diff --git a/src/main.ts b/src/main.ts index bec4a08db..096f00aa4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -13,7 +13,6 @@ import { checkDiskSize, checkPort, MIN_PORT, - checkSDKExtension, } from './input-validator'; import { launchEmulator, killEmulator } from './emulator-manager'; import * as exec from '@actions/exec'; From c8d20e23bc1707ff8a55e958600e73088f838e1c Mon Sep 17 00:00:00 2001 From: Timothy <6560631+TimoPtr@users.noreply.github.com> Date: Fri, 28 Mar 2025 13:41:22 +0100 Subject: [PATCH 7/7] Apply suggestions --- .github/workflows/manually.yml | 4 ++-- action.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/manually.yml b/.github/workflows/manually.yml index eb70fdb70..3a99f089b 100644 --- a/.github/workflows/manually.yml +++ b/.github/workflows/manually.yml @@ -7,11 +7,11 @@ on: 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)' + 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' + 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 diff --git a/action.yml b/action.yml index 7a6e370cb..35dcccd62 100644 --- a/action.yml +++ b/action.yml @@ -6,10 +6,10 @@ branding: 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. 23 for Android Marshmallow, 29 for Android 10. If not set the `api-level` input will be used.' + 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, google_apis_playstore, aosp_atd, google_atd, android-wear, android-wear-cn, android-tv, google-tv, android-automotive, android-automotive-playstore or android-desktop'