Skip to content

Commit b530d96

Browse files
committed
Merge branch 'main' into release/v2
* main: Prepare for release 2.35.0. docs: update AVD profile description (#452) README: Fix imbalanced backtick in `Configurations` table (#445) fix: allow google_apis_ps16k as a valid target (#440) Fix `pre-emulator-launch-script` (#439) Optimize config.ini updates and efficiency improvements report (#436) Fix outdated information about larger runners billing (#437)
2 parents 1dcd009 + 016d4d0 commit b530d96

File tree

10 files changed

+127
-194
lines changed

10 files changed

+127
-194
lines changed

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22

33
## Unreleased
44

5-
No changes yet.
5+
## v2.35.0
6+
7+
* Optimize config.ini updates and efficiency improvements report (#436).
8+
* Fix `pre-emulator-launch-script` (#439).
9+
* Allow `google_apis_ps16k` as a valid target (#440).
610

711
## v2.34.0
812

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ This presents a challenge when running emulators on CI especially when running e
1212

1313
## Running hardware accelerated emulators on Linux runners
1414

15-
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/) which is [free for public GitHub repos](https://github.blog/2024-01-17-github-hosted-runners-double-the-power-for-open-source/). 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:
15+
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:
1616

1717
```
1818
- name: Enable KVM group perms
@@ -90,7 +90,7 @@ jobs:
9090
api-level: ${{ matrix.api-level }}
9191
target: ${{ matrix.target }}
9292
arch: x86_64
93-
profile: Nexus 6
93+
profile: pixel_7_pro
9494
script: ./gradlew connectedCheck
9595
```
9696
@@ -206,9 +206,9 @@ jobs:
206206
|-|-|-|-|
207207
| `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**. |
208208
| `system-image-api-level` | Optional | same as `api-level` | API level of the system image - e.g. `34-ext10`, `35-ext15`. |
209-
| `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`. |
209+
| `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. |
210210
| `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). |
211-
| `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`. |
211+
| `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`. |
212212
| `cores` | Optional | 2 | Number of cores to use for the emulator (`hw.cpu.ncore` in config.ini). |
213213
| `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` |
214214
| `heap-size` | Optional | N/A | Heap size to use for this AVD, in KB or MB, denoted with K or M. - e.g. `512M` |

__tests__/input-validator.test.ts

Lines changed: 0 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,6 @@
11
import * as validator from '../src/input-validator';
22
import { MAX_PORT, MIN_PORT } from '../src/input-validator';
33

4-
describe('target validator tests', () => {
5-
it('Throws if target is unknown', () => {
6-
const func = () => {
7-
validator.checkTarget('some-target');
8-
};
9-
expect(func).toThrowError(`Value for input.target 'some-target' is unknown. Supported options: ${validator.VALID_TARGETS}`);
10-
});
11-
12-
it('Validates successfully with valid target', () => {
13-
const func1 = () => {
14-
validator.checkTarget('default');
15-
};
16-
expect(func1).not.toThrow();
17-
18-
const func2 = () => {
19-
validator.checkTarget('google_apis');
20-
};
21-
expect(func2).not.toThrow();
22-
23-
const func3 = () => {
24-
validator.checkTarget('aosp_atd');
25-
};
26-
expect(func3).not.toThrow();
27-
28-
const func4 = () => {
29-
validator.checkTarget('google_atd');
30-
};
31-
expect(func4).not.toThrow();
32-
33-
const func5 = () => {
34-
validator.checkTarget('google_apis_playstore');
35-
};
36-
expect(func5).not.toThrow();
37-
38-
const func6 = () => {
39-
validator.checkTarget('android-wear');
40-
};
41-
expect(func6).not.toThrow();
42-
43-
const func7 = () => {
44-
validator.checkTarget('android-wear-cn');
45-
};
46-
expect(func7).not.toThrow();
47-
48-
const func8 = () => {
49-
validator.checkTarget('android-tv');
50-
};
51-
expect(func8).not.toThrow();
52-
53-
const func9 = () => {
54-
validator.checkTarget('google-tv');
55-
};
56-
expect(func9).not.toThrow();
57-
58-
const func10 = () => {
59-
validator.checkTarget('android-automotive');
60-
};
61-
expect(func10).not.toThrow();
62-
63-
const func11 = () => {
64-
validator.checkTarget('android-automotive-playstore');
65-
};
66-
expect(func11).not.toThrow();
67-
68-
const func12 = () => {
69-
validator.checkTarget('android-desktop');
70-
};
71-
expect(func12).not.toThrow();
72-
});
73-
});
74-
754
describe('arch validator tests', () => {
765
it('Throws if arch is unknown', () => {
776
const func = () => {

action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ inputs:
1212
description: 'API level of the system image - e.g. 34-ext10, 35-ext15. If not set the `api-level` input will be used.'
1313
required: false
1414
target:
15-
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'
15+
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'
1616
default: 'default'
1717
arch:
1818
description: 'CPU architecture of the system image - x86, x86_64 or arm64-v8a'

lib/emulator-manager.js

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
3232
});
3333
};
3434
Object.defineProperty(exports, "__esModule", { value: true });
35-
exports.killEmulator = exports.launchEmulator = void 0;
35+
exports.killEmulator = exports.launchEmulator = exports.createAvd = void 0;
3636
const exec = __importStar(require("@actions/exec"));
3737
const fs = __importStar(require("fs"));
3838
/**
39-
* Creates and launches a new AVD instance with the specified configurations.
39+
* Creates a new AVD instance with the specified configurations.
4040
*/
41-
function launchEmulator(systemImageApiLevel, target, arch, profile, cores, ramSize, heapSize, sdcardPathOrSize, diskSize, avdName, forceAvdCreation, emulatorBootTimeout, port, emulatorOptions, disableAnimations, disableSpellChecker, disableLinuxHardwareAcceleration, enableHardwareKeyboard) {
41+
function createAvd(arch, avdName, cores, diskSize, enableHardwareKeyboard, forceAvdCreation, heapSize, profile, ramSize, sdcardPathOrSize, systemImageApiLevel, target) {
4242
return __awaiter(this, void 0, void 0, function* () {
4343
try {
44-
console.log(`::group::Launch Emulator`);
44+
console.log(`::group::Create AVD`);
4545
// create a new AVD if AVD directory does not already exist or forceAvdCreation is true
4646
const avdPath = `${process.env.ANDROID_AVD_HOME}/${avdName}.avd`;
4747
if (!fs.existsSync(avdPath) || forceAvdCreation) {
@@ -50,21 +50,42 @@ function launchEmulator(systemImageApiLevel, target, arch, profile, cores, ramSi
5050
console.log(`Creating AVD.`);
5151
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}"`);
5252
}
53-
if (cores) {
54-
yield exec.exec(`sh -c \\"printf 'hw.cpu.ncore=${cores}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
55-
}
56-
if (ramSize) {
57-
yield exec.exec(`sh -c \\"printf 'hw.ramSize=${ramSize}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
58-
}
59-
if (heapSize) {
60-
yield exec.exec(`sh -c \\"printf 'hw.heapSize=${heapSize}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
61-
}
62-
if (enableHardwareKeyboard) {
63-
yield exec.exec(`sh -c \\"printf 'hw.keyboard=yes\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
64-
}
65-
if (diskSize) {
66-
yield exec.exec(`sh -c \\"printf 'disk.dataPartition.size=${diskSize}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
53+
if (cores || ramSize || heapSize || enableHardwareKeyboard || diskSize) {
54+
const configEntries = [];
55+
if (cores) {
56+
configEntries.push(`hw.cpu.ncore=${cores}`);
57+
}
58+
if (ramSize) {
59+
configEntries.push(`hw.ramSize=${ramSize}`);
60+
}
61+
if (heapSize) {
62+
configEntries.push(`hw.heapSize=${heapSize}`);
63+
}
64+
if (enableHardwareKeyboard) {
65+
configEntries.push('hw.keyboard=yes');
66+
}
67+
if (diskSize) {
68+
configEntries.push(`disk.dataPartition.size=${diskSize}`);
69+
}
70+
if (configEntries.length > 0) {
71+
const configContent = configEntries.join('\\n') + '\\n';
72+
yield exec.exec(`sh -c \\"printf '${configContent}' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini"`);
73+
}
6774
}
75+
}
76+
finally {
77+
console.log(`::endgroup::`);
78+
}
79+
});
80+
}
81+
exports.createAvd = createAvd;
82+
/**
83+
* Launches an existing AVD instance with the specified configurations.
84+
*/
85+
function launchEmulator(avdName, disableAnimations, disableLinuxHardwareAcceleration, disableSpellChecker, emulatorBootTimeout, emulatorOptions, enableHardwareKeyboard, port) {
86+
return __awaiter(this, void 0, void 0, function* () {
87+
try {
88+
console.log(`::group::Launch Emulator`);
6889
// turn off hardware acceleration on Linux
6990
if (process.platform === 'linux' && disableLinuxHardwareAcceleration) {
7091
console.log('Disabling Linux hardware acceleration.');

lib/input-validator.js

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,21 @@
11
"use strict";
22
Object.defineProperty(exports, "__esModule", { value: true });
3-
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;
3+
exports.checkDiskSize = exports.checkEmulatorBuild = exports.checkEnableHardwareKeyboard = exports.checkDisableLinuxHardwareAcceleration = exports.checkDisableSpellchecker = exports.checkDisableAnimations = exports.checkPort = exports.checkForceAvdCreation = exports.checkChannel = exports.checkArch = exports.playstoreTargetSubstitution = exports.MAX_PORT = exports.MIN_PORT = exports.VALID_CHANNELS = exports.VALID_ARCHS = exports.MIN_API_LEVEL = void 0;
44
exports.MIN_API_LEVEL = 15;
5-
exports.VALID_TARGETS = [
6-
'default',
7-
'google_apis',
8-
'aosp_atd',
9-
'google_atd',
10-
'google_apis_playstore',
11-
'android-wear',
12-
'android-wear-cn',
13-
'android-tv',
14-
'google-tv',
15-
'android-automotive',
16-
'android-automotive-playstore',
17-
'android-desktop',
18-
];
195
exports.VALID_ARCHS = ['x86', 'x86_64', 'arm64-v8a'];
206
exports.VALID_CHANNELS = ['stable', 'beta', 'dev', 'canary'];
217
exports.MIN_PORT = 5554;
228
exports.MAX_PORT = 5584;
23-
function checkTarget(target) {
24-
if (!exports.VALID_TARGETS.includes(target)) {
25-
throw new Error(`Value for input.target '${target}' is unknown. Supported options: ${exports.VALID_TARGETS}.`);
26-
}
9+
function playstoreTargetSubstitution(target) {
10+
// "playstore" is an allowed shorthand for "google_apis_playstore" images
11+
// this is idempotent - return same even if run multiple times on same target
12+
if (target === 'playstore')
13+
return 'google_apis_playstore';
14+
if (target === 'playstore_ps16k')
15+
return 'google_apis_playstore_ps16k';
16+
return target;
2717
}
28-
exports.checkTarget = checkTarget;
18+
exports.playstoreTargetSubstitution = playstoreTargetSubstitution;
2919
function checkArch(arch) {
3020
if (!exports.VALID_ARCHS.includes(arch)) {
3121
throw new Error(`Value for input.arch '${arch}' is unknown. Supported options: ${exports.VALID_ARCHS}.`);

lib/main.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,7 @@ function run() {
7070
}
7171
console.log(`System image API level: ${systemImageApiLevel}`);
7272
// target of the system image
73-
const targetInput = core.getInput('target');
74-
const target = targetInput == 'playstore' ? 'google_apis_playstore' : targetInput;
75-
(0, input_validator_1.checkTarget)(target);
73+
const target = (0, input_validator_1.playstoreTargetSubstitution)(core.getInput('target'));
7674
console.log(`target: ${target}`);
7775
// CPU architecture of the system image
7876
const arch = core.getInput('arch');
@@ -184,6 +182,8 @@ function run() {
184182
console.log(`::endgroup::`);
185183
// install SDK
186184
yield (0, sdk_installer_1.installAndroidSdk)(apiLevel, systemImageApiLevel, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion);
185+
// create AVD
186+
yield (0, emulator_manager_1.createAvd)(arch, avdName, cores, diskSize, enableHardwareKeyboard, forceAvdCreation, heapSize, profile, ramSize, sdcardPathOrSize, systemImageApiLevel, target);
187187
// execute pre emulator launch script if set
188188
if (preEmulatorLaunchScripts !== undefined) {
189189
console.log(`::group::Run pre emulator launch script`);
@@ -202,7 +202,7 @@ function run() {
202202
console.log(`::endgroup::`);
203203
}
204204
// launch an emulator
205-
yield (0, emulator_manager_1.launchEmulator)(systemImageApiLevel, target, arch, profile, cores, ramSize, heapSize, sdcardPathOrSize, diskSize, avdName, forceAvdCreation, emulatorBootTimeout, port, emulatorOptions, disableAnimations, disableSpellchecker, disableLinuxHardwareAcceleration, enableHardwareKeyboard);
205+
yield (0, emulator_manager_1.launchEmulator)(avdName, disableAnimations, disableLinuxHardwareAcceleration, disableSpellchecker, emulatorBootTimeout, emulatorOptions, enableHardwareKeyboard, port);
206206
// execute the custom script
207207
try {
208208
// move to custom working directory if set

src/emulator-manager.ts

Lines changed: 51 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,24 @@ import * as exec from '@actions/exec';
22
import * as fs from 'fs';
33

44
/**
5-
* Creates and launches a new AVD instance with the specified configurations.
5+
* Creates a new AVD instance with the specified configurations.
66
*/
7-
export async function launchEmulator(
8-
systemImageApiLevel: string,
9-
target: string,
7+
export async function createAvd(
108
arch: string,
11-
profile: string,
9+
avdName: string,
1210
cores: string,
13-
ramSize: string,
14-
heapSize: string,
15-
sdcardPathOrSize: string,
1611
diskSize: string,
17-
avdName: string,
12+
enableHardwareKeyboard: boolean,
1813
forceAvdCreation: boolean,
19-
emulatorBootTimeout: number,
20-
port: number,
21-
emulatorOptions: string,
22-
disableAnimations: boolean,
23-
disableSpellChecker: boolean,
24-
disableLinuxHardwareAcceleration: boolean,
25-
enableHardwareKeyboard: boolean
14+
heapSize: string,
15+
profile: string,
16+
ramSize: string,
17+
sdcardPathOrSize: string,
18+
systemImageApiLevel: string,
19+
target: string
2620
): Promise<void> {
2721
try {
28-
console.log(`::group::Launch Emulator`);
22+
console.log(`::group::Create AVD`);
2923
// create a new AVD if AVD directory does not already exist or forceAvdCreation is true
3024
const avdPath = `${process.env.ANDROID_AVD_HOME}/${avdName}.avd`;
3125
if (!fs.existsSync(avdPath) || forceAvdCreation) {
@@ -37,25 +31,50 @@ export async function launchEmulator(
3731
);
3832
}
3933

40-
if (cores) {
41-
await exec.exec(`sh -c \\"printf 'hw.cpu.ncore=${cores}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
42-
}
43-
44-
if (ramSize) {
45-
await exec.exec(`sh -c \\"printf 'hw.ramSize=${ramSize}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
46-
}
34+
if (cores || ramSize || heapSize || enableHardwareKeyboard || diskSize) {
35+
const configEntries: string[] = [];
4736

48-
if (heapSize) {
49-
await exec.exec(`sh -c \\"printf 'hw.heapSize=${heapSize}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
50-
}
37+
if (cores) {
38+
configEntries.push(`hw.cpu.ncore=${cores}`);
39+
}
40+
if (ramSize) {
41+
configEntries.push(`hw.ramSize=${ramSize}`);
42+
}
43+
if (heapSize) {
44+
configEntries.push(`hw.heapSize=${heapSize}`);
45+
}
46+
if (enableHardwareKeyboard) {
47+
configEntries.push('hw.keyboard=yes');
48+
}
49+
if (diskSize) {
50+
configEntries.push(`disk.dataPartition.size=${diskSize}`);
51+
}
5152

52-
if (enableHardwareKeyboard) {
53-
await exec.exec(`sh -c \\"printf 'hw.keyboard=yes\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
53+
if (configEntries.length > 0) {
54+
const configContent = configEntries.join('\\n') + '\\n';
55+
await exec.exec(`sh -c \\"printf '${configContent}' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini"`);
56+
}
5457
}
58+
} finally {
59+
console.log(`::endgroup::`);
60+
}
61+
}
5562

56-
if (diskSize) {
57-
await exec.exec(`sh -c \\"printf 'disk.dataPartition.size=${diskSize}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
58-
}
63+
/**
64+
* Launches an existing AVD instance with the specified configurations.
65+
*/
66+
export async function launchEmulator(
67+
avdName: string,
68+
disableAnimations: boolean,
69+
disableLinuxHardwareAcceleration: boolean,
70+
disableSpellChecker: boolean,
71+
emulatorBootTimeout: number,
72+
emulatorOptions: string,
73+
enableHardwareKeyboard: boolean,
74+
port: number
75+
): Promise<void> {
76+
try {
77+
console.log(`::group::Launch Emulator`);
5978

6079
// turn off hardware acceleration on Linux
6180
if (process.platform === 'linux' && disableLinuxHardwareAcceleration) {

0 commit comments

Comments
 (0)