Support choosing channel to download SDK components from.

This commit is contained in:
Yang
2021-08-28 19:15:33 +10:00
parent 1ded27b164
commit 1427331a62
14 changed files with 206 additions and 17 deletions
+77
View File
@@ -0,0 +1,77 @@
name: Manually triggered workflow
on:
workflow_dispatch:
inputs:
os:
description: 'OS'
required: true
default: 'macos-latest'
api-level:
description: 'API level of the platform and system image'
required: true
default: '30'
target:
description: 'CPU architecture of the system image - x86, x86_64 or arm64-v8a'
required: true
default: 'default'
os:
description: 'OS'
required: true
default: 'macos-latest'
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@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: ${{ github.event.inputs.api-level }}
target: ${{ github.event.inputs.target }}
arch: ${{ github.event.inputs.arch }}
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 }}
+2 -3
View File
@@ -143,7 +143,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`, `google_apis_playstore`, `android-wear`, `android-wear-cn`, `android-tv` or `google-tv`. |
| `target` | Optional | `default` | Target of the system image - `default`, `google_apis` or `playstore`. |
| `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` and refer to the results under "Available Android Virtual Devices". |
| `cores` | Optional | 2 | Number of cores to use for the emulator (`hw.cpu.ncore` in config.ini). |
@@ -159,6 +159,7 @@ jobs:
| `working-directory` | Optional | `./` | A custom working directory - e.g. `./android` if your root Gradle project is under the `./android` sub-directory within your repository. |
| `ndk` | Optional | N/A | Version of NDK to install - e.g. `21.0.6113669` |
| `cmake` | Optional | N/A | Version of CMake to install - e.g. `3.10.2.4988404` |
| `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` |
Default `emulator-options`: `-no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim`.
@@ -201,7 +202,5 @@ These are some of the open-source projects using (or used) **Android Emulator Ru
- [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)
- [TiagoMSSantos/MobileRT](https://github.com/TiagoMSSantos/MobileRT/blob/master/.github/workflows/android.yml)
- [realm/realm-js](https://github.com/realm/realm-js/blob/master/.github/workflows/integration-tests.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.
+17
View File
@@ -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);
});
});
+31
View File
@@ -102,6 +102,37 @@ 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 = () => {
+3
View File
@@ -49,6 +49,9 @@ 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
+21
View File
@@ -0,0 +1,21 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getChannelId = void 0;
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}'.`);
}
}
exports.getChannelId = getChannelId;
+8 -1
View File
@@ -1,9 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.checkEmulatorBuild = exports.checkDisableLinuxHardwareAcceleration = exports.checkDisableSpellchecker = exports.checkDisableAnimations = exports.checkForceAvdCreation = exports.checkArch = exports.checkTarget = exports.checkApiLevel = exports.VALID_ARCHS = exports.VALID_TARGETS = exports.MIN_API_LEVEL = void 0;
exports.checkEmulatorBuild = exports.checkDisableLinuxHardwareAcceleration = exports.checkDisableSpellchecker = exports.checkDisableAnimations = exports.checkForceAvdCreation = exports.checkChannel = exports.checkArch = exports.checkTarget = exports.checkApiLevel = 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', 'google_apis_playstore', 'android-wear', 'android-wear-cn', 'android-tv', 'google-tv'];
exports.VALID_ARCHS = ['x86', 'x86_64', 'arm64-v8a'];
exports.VALID_CHANNELS = ['stable', 'beta', 'dev', 'canary'];
function checkApiLevel(apiLevel) {
if (isNaN(Number(apiLevel)) || !Number.isInteger(Number(apiLevel))) {
throw new Error(`Unexpected API level: '${apiLevel}'.`);
@@ -25,6 +26,12 @@ function checkArch(arch) {
}
}
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}.`);
}
}
exports.checkChannel = checkChannel;
function checkForceAvdCreation(forceAvdCreation) {
if (!isValidBoolean(forceAvdCreation)) {
throw new Error(`Input for input.force-avd-creation should be either 'true' or 'false'.`);
+7 -1
View File
@@ -34,6 +34,7 @@ const input_validator_1 = require("./input-validator");
const emulator_manager_1 = require("./emulator-manager");
const exec = __importStar(require("@actions/exec"));
const script_parser_1 = require("./script-parser");
const channel_id_mapper_1 = require("./channel-id-mapper");
function run() {
return __awaiter(this, void 0, void 0, function* () {
try {
@@ -123,6 +124,11 @@ function run() {
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');
input_validator_1.checkChannel(channelName);
const channelId = channel_id_mapper_1.getChannelId(channelName);
console.log(`Channel: ${channelId} (${channelName})`);
// custom script to run
const scriptInput = core.getInput('script', { required: true });
const scripts = script_parser_1.parseScript(scriptInput);
@@ -131,7 +137,7 @@ function run() {
console.log(`${script}`);
}));
// install SDK
yield sdk_installer_1.installAndroidSdk(apiLevel, target, arch, emulatorBuild, ndkVersion, cmakeVersion);
yield sdk_installer_1.installAndroidSdk(apiLevel, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion);
// launch an emulator
yield emulator_manager_1.launchEmulator(apiLevel, target, arch, profile, cores, ramSize, sdcardPathOrSize, avdName, forceAvdCreation, emulatorOptions, disableAnimations, disableSpellchecker, disableLinuxHardwareAcceleration);
// execute the custom script
+5 -5
View File
@@ -41,7 +41,7 @@ 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, emulatorBuild, ndkVersion, cmakeVersion) {
function installAndroidSdk(apiLevel, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion) {
return __awaiter(this, void 0, void 0, function* () {
const isOnMac = process.platform === 'darwin';
if (!isOnMac) {
@@ -64,7 +64,7 @@ function installAndroidSdk(apiLevel, target, arch, emulatorBuild, ndkVersion, cm
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"`);
console.log('Installing latest emulator.');
yield exec.exec(`sh -c \\"sdkmanager --install emulator > /dev/null"`);
yield exec.exec(`sh -c \\"sdkmanager --install emulator --channel=${channelId} > /dev/null"`);
if (emulatorBuild) {
console.log(`Installing emulator build ${emulatorBuild}.`);
// TODO find out the correct download URLs for all build ids
@@ -74,14 +74,14 @@ function installAndroidSdk(apiLevel, target, arch, emulatorBuild, ndkVersion, cm
yield io.rmRF('emulator.zip');
}
console.log('Installing system images.');
yield exec.exec(`sh -c \\"sdkmanager --install 'system-images;android-${apiLevel};${target};${arch}' > /dev/null"`);
yield exec.exec(`sh -c \\"sdkmanager --install 'system-images;android-${apiLevel};${target};${arch}' --channel=${channelId} > /dev/null"`);
if (ndkVersion) {
console.log(`Installing NDK ${ndkVersion}.`);
yield exec.exec(`sh -c \\"sdkmanager --install 'ndk;${ndkVersion}' > /dev/null"`);
yield 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"`);
yield exec.exec(`sh -c \\"sdkmanager --install 'cmake;${cmakeVersion}' --channel=${channelId} > /dev/null"`);
}
});
}
+13
View File
@@ -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}'.`);
}
}
+7
View File
@@ -1,6 +1,7 @@
export const MIN_API_LEVEL = 15;
export const VALID_TARGETS: Array<string> = ['default', 'google_apis', 'google_apis_playstore', 'android-wear', 'android-wear-cn', 'android-tv', 'google-tv'];
export const VALID_ARCHS: Array<string> = ['x86', 'x86_64', 'arm64-v8a'];
export const VALID_CHANNELS: Array<string> = ['stable', 'beta', 'dev', 'canary'];
export function checkApiLevel(apiLevel: string): void {
if (isNaN(Number(apiLevel)) || !Number.isInteger(Number(apiLevel))) {
@@ -23,6 +24,12 @@ 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'.`);
+10 -2
View File
@@ -8,11 +8,13 @@ import {
checkEmulatorBuild,
checkDisableSpellchecker,
checkDisableLinuxHardwareAcceleration,
checkForceAvdCreation
checkForceAvdCreation,
checkChannel
} from './input-validator';
import { launchEmulator, killEmulator } from './emulator-manager';
import * as exec from '@actions/exec';
import { parseScript } from './script-parser';
import { getChannelId } from './channel-id-mapper';
async function run() {
try {
@@ -121,6 +123,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);
@@ -130,7 +138,7 @@ async function run() {
});
// install SDK
await installAndroidSdk(apiLevel, target, arch, emulatorBuild, ndkVersion, cmakeVersion);
await installAndroidSdk(apiLevel, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion);
// launch an emulator
await launchEmulator(
+5 -5
View File
@@ -12,7 +12,7 @@ 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: number, target: string, arch: string, emulatorBuild?: string, ndkVersion?: string, cmakeVersion?: string): Promise<void> {
export async function installAndroidSdk(apiLevel: number, target: string, arch: string, channelId: number, emulatorBuild?: string, ndkVersion?: string, cmakeVersion?: string): Promise<void> {
const isOnMac = process.platform === 'darwin';
if (!isOnMac) {
@@ -42,7 +42,7 @@ export async function installAndroidSdk(apiLevel: number, target: string, arch:
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 > /dev/null"`);
await exec.exec(`sh -c \\"sdkmanager --install emulator --channel=${channelId} > /dev/null"`);
if (emulatorBuild) {
console.log(`Installing emulator build ${emulatorBuild}.`);
@@ -53,14 +53,14 @@ export async function installAndroidSdk(apiLevel: number, 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}' > /dev/null"`);
await exec.exec(`sh -c \\"sdkmanager --install 'system-images;android-${apiLevel};${target};${arch}' --channel=${channelId} > /dev/null"`);
if (ndkVersion) {
console.log(`Installing NDK ${ndkVersion}.`);
await 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}.`);
await exec.exec(`sh -c \\"sdkmanager --install 'cmake;${cmakeVersion}' > /dev/null"`);
await exec.exec(`sh -c \\"sdkmanager --install 'cmake;${cmakeVersion}' --channel=${channelId} > /dev/null"`);
}
}