Compare commits

...

12 Commits

Author SHA1 Message Date
Yang 94bd1ed921 Merge branch 'main' into release/v2
* main:
  Prepare for release 2.14.2.
  Support non-default API 28 images
2021-01-05 18:29:16 +11:00
Yang c3e261e13b Prepare for release 2.14.2. 2021-01-05 18:28:29 +11:00
linl33 434e949c3b Support non-default API 28 images 2021-01-05 18:21:57 +11:00
Yang 70e3f6e680 Merge branch 'main' into release/v2
* main:
  Prepare for release 2.14.1.
  Fix AVD creation hang when profile is not specified.
2020-12-28 14:00:37 +11:00
Yang 0d8602fe47 Prepare for release 2.14.1. 2020-12-28 13:59:41 +11:00
Yang 28373d3d4f Fix AVD creation hang when profile is not specified. 2020-12-28 13:13:03 +11:00
Yang 02cf805290 Merge branch 'main' into release/v2
* main:
  Prepare for release 2.14.0.
  Support specifying SD card path or size.
  Update npm packages.
  Replace usages of deprecated ANDROID_HOME with ANDROID_ROOT_SDK. Bump build tools to 30.0.3. Update test-fixture dependencies.
  Update Compose Samples URL
2020-12-27 20:48:59 +11:00
Yang 045b7d8c5e Prepare for release 2.14.0. 2020-12-27 20:46:12 +11:00
Yang b0f3c521a5 Support specifying SD card path or size. 2020-12-27 20:37:13 +11:00
Yang 308aa234e1 Update npm packages. 2020-12-25 00:24:38 +11:00
Yang d4b3926c7d Replace usages of deprecated ANDROID_HOME with ANDROID_ROOT_SDK.
Bump build tools to 30.0.3.
Update test-fixture dependencies.
2020-12-25 00:05:51 +11:00
Vitus 877a4c47c3 Update Compose Samples URL 2020-12-11 03:24:04 +11:00
25 changed files with 2999 additions and 452 deletions
+4
View File
@@ -36,6 +36,9 @@ jobs:
- os: macos-latest - os: macos-latest
api-level: 24 api-level: 24
target: playstore target: playstore
- os: macos-latest
api-level: 28
target: google_apis
steps: steps:
- name: checkout - name: checkout
@@ -68,6 +71,7 @@ jobs:
target: ${{ matrix.target }} target: ${{ matrix.target }}
arch: x86 arch: x86
profile: Nexus 6 profile: Nexus 6
sdcard-path-or-size: 100M
avd-name: test avd-name: test
emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim -camera-back none emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim -camera-back none
disable-animations: true disable-animations: true
+14
View File
@@ -1,5 +1,19 @@
# Change Log # Change Log
## v2.14.2
* Support API 28 system images with `google_apis` or `google_apis_playstore` target - [#117](https://github.com/ReactiveCircus/android-emulator-runner/pull/117).
## v2.14.1
* Fix hang during AVD creation when `profile` is not specified - [#113](https://github.com/ReactiveCircus/android-emulator-runner/issues/113).
## v2.14.0
* Support specifying SD card path or size via `sdcard-path-or-size`.
* Update npm packages.
* Remove usages of deprecated `$ANDROID_HOME`.
## v2.13.0 ## v2.13.0
* Updated to SDK command-line tools `3.0`. * Updated to SDK command-line tools `3.0`.
+2 -1
View File
@@ -92,6 +92,7 @@ jobs:
| `target` | Optional | `default` | Target of the system image - `default`, `google_apis` or `playstore`. | | `target` | Optional | `default` | Target of the system image - `default`, `google_apis` or `playstore`. |
| `arch` | Optional | `x86` | CPU architecture of the system image - `x86` or `x86_64`. Note that `x86_64` image is only available for API 21+. | | `arch` | Optional | `x86` | CPU architecture of the system image - `x86` or `x86_64`. Note that `x86_64` image is only available for API 21+. |
| `profile` | Optional | N/A | Hardware profile used for creating the AVD - e.g. `Nexus 6`. For a list of all profiles available, run `avdmanager list` and refer to the results under "Available Android Virtual Devices". | | `profile` | Optional | N/A | Hardware profile used for creating the AVD - e.g. `Nexus 6`. For a list of all profiles available, run `avdmanager list` and refer to the results under "Available Android Virtual Devices". |
| `sdcard-path-or-size` | Optional | N/A | Path to the SD card image for this AVD or the size of a new SD card image to create for this AVD, in KB or MB, denoted with K or M. - e.g. `path/to/sdcard`, or `1000M`. |
| `avd-name` | Optional | `test` | Custom AVD name used for creating the Android Virtual Device. | | `avd-name` | Optional | `test` | Custom AVD name used for creating the Android Virtual Device. |
| `emulator-options` | Optional | See below | Command-line options used when launching the emulator (replacing all default options) - e.g. `-no-window -no-snapshot -camera-back emulated`. | | `emulator-options` | Optional | See below | Command-line options used when launching the emulator (replacing all default options) - e.g. `-no-window -no-snapshot -camera-back emulated`. |
| `disable-animations` | Optional | `true` | Whether to disable animations - `true` or `false`. | | `disable-animations` | Optional | `true` | Whether to disable animations - `true` or `false`. |
@@ -122,7 +123,7 @@ These are some of the open-source projects using (or used) **Android Emulator Ru
- [natario1/Transcoder](https://github.com/natario1/Transcoder/tree/master/.github/workflows) - [natario1/Transcoder](https://github.com/natario1/Transcoder/tree/master/.github/workflows)
- [chrisbanes/insetter](https://github.com/chrisbanes/insetter/tree/main/.github/workflows) - [chrisbanes/insetter](https://github.com/chrisbanes/insetter/tree/main/.github/workflows)
- [slackhq/keeper](https://github.com/slackhq/keeper/tree/main/.github/workflows) - [slackhq/keeper](https://github.com/slackhq/keeper/tree/main/.github/workflows)
- [android/compose-samples](https://github.com/android/compose-samples/blob/master/.github/workflows/ci.yaml) - [android/compose-samples](https://github.com/android/compose-samples/tree/main/.github/workflows)
- [ReactiveCircus/streamlined](https://github.com/ReactiveCircus/streamlined/tree/main/.github/workflows) - [ReactiveCircus/streamlined](https://github.com/ReactiveCircus/streamlined/tree/main/.github/workflows)
- [ReactiveCircus/FlowBinding](https://github.com/ReactiveCircus/FlowBinding) - [ReactiveCircus/FlowBinding](https://github.com/ReactiveCircus/FlowBinding)
- [JakeWharton/RxBinding](https://github.com/JakeWharton/RxBinding/tree/master/.github/workflows) - [JakeWharton/RxBinding](https://github.com/JakeWharton/RxBinding/tree/master/.github/workflows)
+2
View File
@@ -16,6 +16,8 @@ inputs:
default: 'x86' default: 'x86'
profile: profile:
description: 'hardware profile used for creating the AVD - e.g. `Nexus 6`' description: 'hardware profile used for creating the AVD - e.g. `Nexus 6`'
sdcard-path-or-size:
description: 'path to the SD card image for this AVD or the size of a new SD card image to create for this AVD, in KB or MB, denoted with K or M. - e.g. `path/to/sdcard`, or `1000M`'
avd-name: avd-name:
description: 'custom AVD name used for creating the Android Virtual Device' description: 'custom AVD name used for creating the Android Virtual Device'
default: 'test' default: 'test'
+6 -10
View File
@@ -34,24 +34,20 @@ const EMULATOR_BOOT_TIMEOUT_SECONDS = 600;
/** /**
* Creates and launches a new AVD instance with the specified configurations. * Creates and launches a new AVD instance with the specified configurations.
*/ */
function launchEmulator(apiLevel, target, arch, profile, avdName, emulatorOptions, disableAnimations) { function launchEmulator(apiLevel, target, arch, profile, sdcardPathOrSize, avdName, emulatorOptions, disableAnimations) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
// create a new AVD // create a new AVD
if (profile.trim() !== '') { const profileOption = profile.trim() !== '' ? `--device '${profile}'` : '';
console.log(`Creating AVD with custom profile ${profile}`); const sdcardPathOrSizeOption = sdcardPathOrSize.trim() !== '' ? `--sdcard '${sdcardPathOrSize}'` : '';
yield exec.exec(`avdmanager create avd --force -n "${avdName}" --abi "${target}/${arch}" --package "system-images;android-${apiLevel};${target};${arch}" --device "${profile}"`); 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}"`);
else {
console.log(`Creating AVD without custom profile.`);
yield exec.exec(`sh -c \\"echo no | avdmanager create avd --force -n "${avdName}" --abi '${target}/${arch}' --package 'system-images;android-${apiLevel};${target};${arch}'"`);
}
// start emulator // start emulator
console.log('Starting emulator.'); console.log('Starting emulator.');
// turn off hardware acceleration on Linux // turn off hardware acceleration on Linux
if (process.platform === 'linux') { if (process.platform === 'linux') {
emulatorOptions += ' -accel off'; emulatorOptions += ' -accel off';
} }
yield exec.exec(`sh -c \\"${process.env.ANDROID_HOME}/emulator/emulator -avd "${avdName}" ${emulatorOptions} &"`, [], { yield exec.exec(`sh -c \\"${process.env.ANDROID_SDK_ROOT}/emulator/emulator -avd "${avdName}" ${emulatorOptions} &"`, [], {
listeners: { listeners: {
stderr: (data) => { stderr: (data) => {
if (data.toString().includes('invalid command-line parameter')) { if (data.toString().includes('invalid command-line parameter')) {
+4 -1
View File
@@ -63,6 +63,9 @@ function run() {
// Hardware profile used for creating the AVD // Hardware profile used for creating the AVD
const profile = core.getInput('profile'); const profile = core.getInput('profile');
console.log(`Hardware profile: ${profile}`); console.log(`Hardware profile: ${profile}`);
// SD card path or size used for creating the AVD
const sdcardPathOrSize = core.getInput('sdcard-path-or-size');
console.log(`SD card path or size: ${sdcardPathOrSize}`);
// custom name used for creating the AVD // custom name used for creating the AVD
const avdName = core.getInput('avd-name'); const avdName = core.getInput('avd-name');
console.log(`AVD name: ${avdName}`); console.log(`AVD name: ${avdName}`);
@@ -109,7 +112,7 @@ function run() {
// install SDK // install SDK
yield sdk_installer_1.installAndroidSdk(apiLevel, target, arch, emulatorBuild, ndkVersion, cmakeVersion); yield sdk_installer_1.installAndroidSdk(apiLevel, target, arch, emulatorBuild, ndkVersion, cmakeVersion);
// launch an emulator // launch an emulator
yield emulator_manager_1.launchEmulator(apiLevel, target, arch, profile, avdName, emulatorOptions, disableAnimations); yield emulator_manager_1.launchEmulator(apiLevel, target, arch, profile, sdcardPathOrSize, avdName, emulatorOptions, disableAnimations);
// execute the custom script // execute the custom script
try { try {
// move to custom working directory if set // move to custom working directory if set
+13 -10
View File
@@ -34,7 +34,7 @@ const exec = __importStar(require("@actions/exec"));
const io = __importStar(require("@actions/io")); const io = __importStar(require("@actions/io"));
const tc = __importStar(require("@actions/tool-cache")); const tc = __importStar(require("@actions/tool-cache"));
const fs = __importStar(require("fs")); const fs = __importStar(require("fs"));
const BUILD_TOOLS_VERSION = '30.0.2'; const BUILD_TOOLS_VERSION = '30.0.3';
const CMDLINE_TOOLS_URL_MAC = 'https://dl.google.com/android/repository/commandlinetools-mac-6858069_latest.zip'; const CMDLINE_TOOLS_URL_MAC = 'https://dl.google.com/android/repository/commandlinetools-mac-6858069_latest.zip';
const CMDLINE_TOOLS_URL_LINUX = 'https://dl.google.com/android/repository/commandlinetools-linux-6858069_latest.zip'; const CMDLINE_TOOLS_URL_LINUX = 'https://dl.google.com/android/repository/commandlinetools-linux-6858069_latest.zip';
/** /**
@@ -45,9 +45,9 @@ function installAndroidSdk(apiLevel, target, arch, emulatorBuild, ndkVersion, cm
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const isOnMac = process.platform === 'darwin'; const isOnMac = process.platform === 'darwin';
if (!isOnMac) { if (!isOnMac) {
yield exec.exec(`sh -c \\"sudo chown $USER:$USER ${process.env.ANDROID_HOME} -R`); yield exec.exec(`sh -c \\"sudo chown $USER:$USER ${process.env.ANDROID_SDK_ROOT} -R`);
} }
const cmdlineToolsPath = `${process.env.ANDROID_HOME}/cmdline-tools`; const cmdlineToolsPath = `${process.env.ANDROID_SDK_ROOT}/cmdline-tools`;
if (!fs.existsSync(cmdlineToolsPath)) { if (!fs.existsSync(cmdlineToolsPath)) {
console.log('Installing new cmdline-tools.'); console.log('Installing new cmdline-tools.');
const sdkUrl = isOnMac ? CMDLINE_TOOLS_URL_MAC : CMDLINE_TOOLS_URL_LINUX; const sdkUrl = isOnMac ? CMDLINE_TOOLS_URL_MAC : CMDLINE_TOOLS_URL_LINUX;
@@ -55,16 +55,16 @@ function installAndroidSdk(apiLevel, target, arch, emulatorBuild, ndkVersion, cm
yield tc.extractZip(downloadPath, cmdlineToolsPath); yield tc.extractZip(downloadPath, cmdlineToolsPath);
yield io.mv(`${cmdlineToolsPath}/cmdline-tools`, `${cmdlineToolsPath}/latest`); yield io.mv(`${cmdlineToolsPath}/cmdline-tools`, `${cmdlineToolsPath}/latest`);
// add paths for commandline-tools and platform-tools // add paths for commandline-tools and platform-tools
core.addPath(`${cmdlineToolsPath}/latest:${cmdlineToolsPath}/latest/bin:${process.env.ANDROID_HOME}/platform-tools`); core.addPath(`${cmdlineToolsPath}/latest:${cmdlineToolsPath}/latest/bin:${process.env.ANDROID_SDK_ROOT}/platform-tools`);
} }
// additional permission and license requirements for Linux // additional permission and license requirements for Linux
const sdkPreviewLicensePath = `${process.env.ANDROID_HOME}/licenses/android-sdk-preview-license`; const sdkPreviewLicensePath = `${process.env.ANDROID_SDK_ROOT}/licenses/android-sdk-preview-license`;
if (!isOnMac && !fs.existsSync(sdkPreviewLicensePath)) { if (!isOnMac && !fs.existsSync(sdkPreviewLicensePath)) {
fs.writeFileSync(sdkPreviewLicensePath, '\n84831b9409646a918e30573bab4c9c91346d8abd'); fs.writeFileSync(sdkPreviewLicensePath, '\n84831b9409646a918e30573bab4c9c91346d8abd');
} }
// license required for API 30 system images // license required for API 30 and non-default API 28 system images
const sdkArmDbtLicensePath = `${process.env.ANDROID_HOME}/licenses/android-sdk-arm-dbt-license`; const sdkArmDbtLicensePath = `${process.env.ANDROID_SDK_ROOT}/licenses/android-sdk-arm-dbt-license`;
if (apiLevel == 30 && !fs.existsSync(sdkArmDbtLicensePath)) { if (requiresArmDbtLicense(apiLevel, target) && !fs.existsSync(sdkArmDbtLicensePath)) {
fs.writeFileSync(sdkArmDbtLicensePath, '\n859f317696f67ef3d7f30a50a5560e7834b43903'); fs.writeFileSync(sdkArmDbtLicensePath, '\n859f317696f67ef3d7f30a50a5560e7834b43903');
} }
console.log('Installing latest build tools, platform tools, and platform.'); console.log('Installing latest build tools, platform tools, and platform.');
@@ -72,8 +72,8 @@ function installAndroidSdk(apiLevel, target, arch, emulatorBuild, ndkVersion, cm
if (emulatorBuild) { if (emulatorBuild) {
console.log(`Installing emulator build ${emulatorBuild}.`); console.log(`Installing emulator build ${emulatorBuild}.`);
yield exec.exec(`curl -fo emulator.zip https://dl.google.com/android/repository/emulator-${isOnMac ? 'darwin' : 'linux'}-${emulatorBuild}.zip`); yield exec.exec(`curl -fo emulator.zip https://dl.google.com/android/repository/emulator-${isOnMac ? 'darwin' : 'linux'}-${emulatorBuild}.zip`);
yield io.rmRF(`${process.env.ANDROID_HOME}/emulator`); yield io.rmRF(`${process.env.ANDROID_SDK_ROOT}/emulator`);
yield exec.exec(`unzip -q emulator.zip -d ${process.env.ANDROID_HOME}`); yield exec.exec(`unzip -q emulator.zip -d ${process.env.ANDROID_SDK_ROOT}`);
yield io.rmRF('emulator.zip'); yield io.rmRF('emulator.zip');
} }
else { else {
@@ -93,3 +93,6 @@ function installAndroidSdk(apiLevel, target, arch, emulatorBuild, ndkVersion, cm
}); });
} }
exports.installAndroidSdk = installAndroidSdk; exports.installAndroidSdk = installAndroidSdk;
function requiresArmDbtLicense(apiLevel, target) {
return apiLevel === 30 || (apiLevel === 28 && target !== 'default');
}
+7 -1
View File
@@ -1,5 +1,11 @@
## Releases ## Releases
## 1.0.9
Throw HttpClientError instead of a generic Error from the \<verb>Json() helper methods when the server responds with a non-successful status code.
## 1.0.8
Fixed security issue where a redirect (e.g. 302) to another domain would pass headers. The fix was to strip the authorization header if the hostname was different. More [details in PR #27](https://github.com/actions/http-client/pull/27)
## 1.0.7 ## 1.0.7
Update NPM dependencies and add 429 to the list of HttpCodes Update NPM dependencies and add 429 to the list of HttpCodes
@@ -13,4 +19,4 @@ Adds \<verb>Json() helper methods for json over http scenarios.
Started to add \<verb>Json() helper methods. Do not use this release for that. Use >= 1.0.5 since there was an issue with types. Started to add \<verb>Json() helper methods. Do not use this release for that. Use >= 1.0.5 since there was an issue with types.
## 1.0.1 to 1.0.3 ## 1.0.1 to 1.0.3
Adds proxy support. Adds proxy support.
+5
View File
@@ -42,6 +42,11 @@ export declare enum MediaTypes {
* @param serverUrl The server URL where the request will be sent. For example, https://api.github.com * @param serverUrl The server URL where the request will be sent. For example, https://api.github.com
*/ */
export declare function getProxyUrl(serverUrl: string): string; export declare function getProxyUrl(serverUrl: string): string;
export declare class HttpClientError extends Error {
constructor(message: string, statusCode: number);
statusCode: number;
result?: any;
}
export declare class HttpClientResponse implements ifm.IHttpClientResponse { export declare class HttpClientResponse implements ifm.IHttpClientResponse {
constructor(message: http.IncomingMessage); constructor(message: http.IncomingMessage);
message: http.IncomingMessage; message: http.IncomingMessage;
+17 -13
View File
@@ -1,6 +1,5 @@
"use strict"; "use strict";
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
const url = require("url");
const http = require("http"); const http = require("http");
const https = require("https"); const https = require("https");
const pm = require("./proxy"); const pm = require("./proxy");
@@ -49,7 +48,7 @@ var MediaTypes;
* @param serverUrl The server URL where the request will be sent. For example, https://api.github.com * @param serverUrl The server URL where the request will be sent. For example, https://api.github.com
*/ */
function getProxyUrl(serverUrl) { function getProxyUrl(serverUrl) {
let proxyUrl = pm.getProxyUrl(url.parse(serverUrl)); let proxyUrl = pm.getProxyUrl(new URL(serverUrl));
return proxyUrl ? proxyUrl.href : ''; return proxyUrl ? proxyUrl.href : '';
} }
exports.getProxyUrl = getProxyUrl; exports.getProxyUrl = getProxyUrl;
@@ -68,6 +67,15 @@ const HttpResponseRetryCodes = [
const RetryableHttpVerbs = ['OPTIONS', 'GET', 'DELETE', 'HEAD']; const RetryableHttpVerbs = ['OPTIONS', 'GET', 'DELETE', 'HEAD'];
const ExponentialBackoffCeiling = 10; const ExponentialBackoffCeiling = 10;
const ExponentialBackoffTimeSlice = 5; const ExponentialBackoffTimeSlice = 5;
class HttpClientError extends Error {
constructor(message, statusCode) {
super(message);
this.name = 'HttpClientError';
this.statusCode = statusCode;
Object.setPrototypeOf(this, HttpClientError.prototype);
}
}
exports.HttpClientError = HttpClientError;
class HttpClientResponse { class HttpClientResponse {
constructor(message) { constructor(message) {
this.message = message; this.message = message;
@@ -86,7 +94,7 @@ class HttpClientResponse {
} }
exports.HttpClientResponse = HttpClientResponse; exports.HttpClientResponse = HttpClientResponse;
function isHttps(requestUrl) { function isHttps(requestUrl) {
let parsedUrl = url.parse(requestUrl); let parsedUrl = new URL(requestUrl);
return parsedUrl.protocol === 'https:'; return parsedUrl.protocol === 'https:';
} }
exports.isHttps = isHttps; exports.isHttps = isHttps;
@@ -191,7 +199,7 @@ class HttpClient {
if (this._disposed) { if (this._disposed) {
throw new Error('Client has already been disposed.'); throw new Error('Client has already been disposed.');
} }
let parsedUrl = url.parse(requestUrl); let parsedUrl = new URL(requestUrl);
let info = this._prepareRequest(verb, parsedUrl, headers); let info = this._prepareRequest(verb, parsedUrl, headers);
// Only perform retries on reads since writes may not be idempotent. // Only perform retries on reads since writes may not be idempotent.
let maxTries = this._allowRetries && RetryableHttpVerbs.indexOf(verb) != -1 let maxTries = this._allowRetries && RetryableHttpVerbs.indexOf(verb) != -1
@@ -230,7 +238,7 @@ class HttpClient {
// if there's no location to redirect to, we won't // if there's no location to redirect to, we won't
break; break;
} }
let parsedRedirectUrl = url.parse(redirectUrl); let parsedRedirectUrl = new URL(redirectUrl);
if (parsedUrl.protocol == 'https:' && if (parsedUrl.protocol == 'https:' &&
parsedUrl.protocol != parsedRedirectUrl.protocol && parsedUrl.protocol != parsedRedirectUrl.protocol &&
!this._allowRedirectDowngrade) { !this._allowRedirectDowngrade) {
@@ -346,7 +354,7 @@ class HttpClient {
* @param serverUrl The server URL where the request will be sent. For example, https://api.github.com * @param serverUrl The server URL where the request will be sent. For example, https://api.github.com
*/ */
getAgent(serverUrl) { getAgent(serverUrl) {
let parsedUrl = url.parse(serverUrl); let parsedUrl = new URL(serverUrl);
return this._getAgent(parsedUrl); return this._getAgent(parsedUrl);
} }
_prepareRequest(method, requestUrl, headers) { _prepareRequest(method, requestUrl, headers) {
@@ -419,7 +427,7 @@ class HttpClient {
maxSockets: maxSockets, maxSockets: maxSockets,
keepAlive: this._keepAlive, keepAlive: this._keepAlive,
proxy: { proxy: {
proxyAuth: proxyUrl.auth, proxyAuth: `${proxyUrl.username}:${proxyUrl.password}`,
host: proxyUrl.hostname, host: proxyUrl.hostname,
port: proxyUrl.port port: proxyUrl.port
} }
@@ -514,12 +522,8 @@ class HttpClient {
else { else {
msg = 'Failed request: (' + statusCode + ')'; msg = 'Failed request: (' + statusCode + ')';
} }
let err = new Error(msg); let err = new HttpClientError(msg, statusCode);
// attach statusCode and body obj (if available) to the error object err.result = response.result;
err['statusCode'] = statusCode;
if (response.result) {
err['result'] = response.result;
}
reject(err); reject(err);
} }
else { else {
+1 -2
View File
@@ -1,6 +1,5 @@
/// <reference types="node" /> /// <reference types="node" />
import http = require('http'); import http = require('http');
import url = require('url');
export interface IHeaders { export interface IHeaders {
[key: string]: any; [key: string]: any;
} }
@@ -27,7 +26,7 @@ export interface IHttpClientResponse {
} }
export interface IRequestInfo { export interface IRequestInfo {
options: http.RequestOptions; options: http.RequestOptions;
parsedUrl: url.Url; parsedUrl: URL;
httpModule: any; httpModule: any;
} }
export interface IRequestOptions { export interface IRequestOptions {
+12 -12
View File
@@ -1,33 +1,33 @@
{ {
"_args": [ "_args": [
[ [
"@actions/http-client@1.0.8", "@actions/http-client@1.0.9",
"/Users/yang/Desktop/Projects/android-emulator-runner" "."
] ]
], ],
"_from": "@actions/http-client@1.0.8", "_from": "@actions/http-client@1.0.9",
"_id": "@actions/http-client@1.0.8", "_id": "@actions/http-client@1.0.9",
"_inBundle": false, "_inBundle": false,
"_integrity": "sha512-G4JjJ6f9Hb3Zvejj+ewLLKLf99ZC+9v+yCxoYf9vSyH+WkzPLB2LuUtRMGNkooMqdugGBFStIKXOuvH1W+EctA==", "_integrity": "sha512-0O4SsJ7q+MK0ycvXPl2e6bMXV7dxAXOGjrXS1eTF9s2S401Tp6c/P3c3Joz04QefC1J6Gt942Wl2jbm3f4mLcg==",
"_location": "/@actions/http-client", "_location": "/@actions/http-client",
"_phantomChildren": {}, "_phantomChildren": {},
"_requested": { "_requested": {
"type": "version", "type": "version",
"registry": true, "registry": true,
"raw": "@actions/http-client@1.0.8", "raw": "@actions/http-client@1.0.9",
"name": "@actions/http-client", "name": "@actions/http-client",
"escapedName": "@actions%2fhttp-client", "escapedName": "@actions%2fhttp-client",
"scope": "@actions", "scope": "@actions",
"rawSpec": "1.0.8", "rawSpec": "1.0.9",
"saveSpec": null, "saveSpec": null,
"fetchSpec": "1.0.8" "fetchSpec": "1.0.9"
}, },
"_requiredBy": [ "_requiredBy": [
"/@actions/tool-cache" "/@actions/tool-cache"
], ],
"_resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.8.tgz", "_resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.9.tgz",
"_spec": "1.0.8", "_spec": "1.0.9",
"_where": "/Users/yang/Desktop/Projects/android-emulator-runner", "_where": ".",
"author": { "author": {
"name": "GitHub, Inc." "name": "GitHub, Inc."
}, },
@@ -66,5 +66,5 @@
"format-check": "prettier --check *.ts && prettier --check **/*.ts", "format-check": "prettier --check *.ts && prettier --check **/*.ts",
"test": "jest" "test": "jest"
}, },
"version": "1.0.8" "version": "1.0.9"
} }
+2 -4
View File
@@ -1,4 +1,2 @@
/// <reference types="node" /> export declare function getProxyUrl(reqUrl: URL): URL | undefined;
import * as url from 'url'; export declare function checkBypass(reqUrl: URL): boolean;
export declare function getProxyUrl(reqUrl: url.Url): url.Url | undefined;
export declare function checkBypass(reqUrl: url.Url): boolean;
+1 -2
View File
@@ -1,6 +1,5 @@
"use strict"; "use strict";
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
const url = require("url");
function getProxyUrl(reqUrl) { function getProxyUrl(reqUrl) {
let usingSsl = reqUrl.protocol === 'https:'; let usingSsl = reqUrl.protocol === 'https:';
let proxyUrl; let proxyUrl;
@@ -15,7 +14,7 @@ function getProxyUrl(reqUrl) {
proxyVar = process.env['http_proxy'] || process.env['HTTP_PROXY']; proxyVar = process.env['http_proxy'] || process.env['HTTP_PROXY'];
} }
if (proxyVar) { if (proxyVar) {
proxyUrl = url.parse(proxyVar); proxyUrl = new URL(proxyVar);
} }
return proxyUrl; return proxyUrl;
} }
+9
View File
@@ -0,0 +1,9 @@
The MIT License (MIT)
Copyright 2019 GitHub
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+12 -12
View File
@@ -1,38 +1,38 @@
{ {
"_args": [ "_args": [
[ [
"@actions/tool-cache@1.6.0", "@actions/tool-cache@1.6.1",
"." "."
] ]
], ],
"_from": "@actions/tool-cache@1.6.0", "_from": "@actions/tool-cache@1.6.1",
"_id": "@actions/tool-cache@1.6.0", "_id": "@actions/tool-cache@1.6.1",
"_inBundle": false, "_inBundle": false,
"_integrity": "sha512-+fyEBImPD3m5I0o6DflCO0NHY180LPoX8Lo6y4Iez+V17kO8kfkH0VHxb8mUdmD6hn9dWA9Ch1JA20fXoIYUeQ==", "_integrity": "sha512-F+vwEDwfqcHMKuSkj79pihOnsAMv23EkG76nMpc82UsnXwyQdyEsktGxrB0SNtm7pRqTXEIOoAPTgrSQclXYTg==",
"_location": "/@actions/tool-cache", "_location": "/@actions/tool-cache",
"_phantomChildren": {}, "_phantomChildren": {},
"_requested": { "_requested": {
"type": "version", "type": "version",
"registry": true, "registry": true,
"raw": "@actions/tool-cache@1.6.0", "raw": "@actions/tool-cache@1.6.1",
"name": "@actions/tool-cache", "name": "@actions/tool-cache",
"escapedName": "@actions%2ftool-cache", "escapedName": "@actions%2ftool-cache",
"scope": "@actions", "scope": "@actions",
"rawSpec": "1.6.0", "rawSpec": "1.6.1",
"saveSpec": null, "saveSpec": null,
"fetchSpec": "1.6.0" "fetchSpec": "1.6.1"
}, },
"_requiredBy": [ "_requiredBy": [
"/" "/"
], ],
"_resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-1.6.0.tgz", "_resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-1.6.1.tgz",
"_spec": "1.6.0", "_spec": "1.6.1",
"_where": ".", "_where": ".",
"bugs": { "bugs": {
"url": "https://github.com/actions/toolkit/issues" "url": "https://github.com/actions/toolkit/issues"
}, },
"dependencies": { "dependencies": {
"@actions/core": "^1.2.3", "@actions/core": "^1.2.6",
"@actions/exec": "^1.0.0", "@actions/exec": "^1.0.0",
"@actions/http-client": "^1.0.8", "@actions/http-client": "^1.0.8",
"@actions/io": "^1.0.1", "@actions/io": "^1.0.1",
@@ -54,7 +54,7 @@
"lib", "lib",
"scripts" "scripts"
], ],
"homepage": "https://github.com/actions/toolkit/tree/master/packages/tool-cache", "homepage": "https://github.com/actions/toolkit/tree/main/packages/tool-cache",
"keywords": [ "keywords": [
"github", "github",
"actions", "actions",
@@ -77,5 +77,5 @@
"tsc": "tsc" "tsc": "tsc"
}, },
"types": "lib/tool-cache.d.ts", "types": "lib/tool-cache.d.ts",
"version": "1.6.0" "version": "1.6.1"
} }
+2837 -336
View File
File diff suppressed because it is too large Load Diff
+5 -5
View File
@@ -23,22 +23,22 @@
"dependencies": { "dependencies": {
"@actions/core": "^1.2.6", "@actions/core": "^1.2.6",
"@actions/exec": "^1.0.4", "@actions/exec": "^1.0.4",
"@actions/tool-cache": "^1.6.0", "@actions/tool-cache": "^1.6.1",
"minimist": "^1.2.5" "minimist": "^1.2.5"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^24.9.1", "@types/jest": "^24.9.1",
"@types/node": "^12.19.3", "@types/node": "^12.19.11",
"@typescript-eslint/eslint-plugin": "^2.34.0", "@typescript-eslint/eslint-plugin": "^2.34.0",
"@typescript-eslint/parser": "^2.34.0", "@typescript-eslint/parser": "^2.34.0",
"eslint": "^6.8.0", "eslint": "^6.8.0",
"eslint-config-prettier": "^6.15.0", "eslint-config-prettier": "^6.15.0",
"eslint-plugin-import": "^2.22.1", "eslint-plugin-import": "^2.22.1",
"eslint-plugin-prettier": "^3.1.4", "eslint-plugin-prettier": "^3.3.0",
"jest": "^25.5.4", "jest": "^26.6.3",
"jest-circus": "^25.5.4", "jest-circus": "^25.5.4",
"prettier": "^1.19.1", "prettier": "^1.19.1",
"ts-jest": "^26.4.3", "ts-jest": "^26.4.4",
"typescript": "^3.9.7" "typescript": "^3.9.7"
} }
} }
+17 -9
View File
@@ -5,15 +5,23 @@ const EMULATOR_BOOT_TIMEOUT_SECONDS = 600;
/** /**
* Creates and launches a new AVD instance with the specified configurations. * Creates and launches a new AVD instance with the specified configurations.
*/ */
export async function launchEmulator(apiLevel: number, target: string, arch: string, profile: string, avdName: string, emulatorOptions: string, disableAnimations: boolean): Promise<void> { export async function launchEmulator(
apiLevel: number,
target: string,
arch: string,
profile: string,
sdcardPathOrSize: string,
avdName: string,
emulatorOptions: string,
disableAnimations: boolean
): Promise<void> {
// create a new AVD // create a new AVD
if (profile.trim() !== '') { const profileOption = profile.trim() !== '' ? `--device '${profile}'` : '';
console.log(`Creating AVD with custom profile ${profile}`); const sdcardPathOrSizeOption = sdcardPathOrSize.trim() !== '' ? `--sdcard '${sdcardPathOrSize}'` : '';
await exec.exec(`avdmanager create avd --force -n "${avdName}" --abi "${target}/${arch}" --package "system-images;android-${apiLevel};${target};${arch}" --device "${profile}"`); console.log(`Creating AVD.`);
} else { await exec.exec(
console.log(`Creating AVD without custom profile.`); `sh -c \\"echo no | avdmanager create avd --force -n "${avdName}" --abi '${target}/${arch}' --package 'system-images;android-${apiLevel};${target};${arch}' ${profileOption} ${sdcardPathOrSizeOption}"`
await exec.exec(`sh -c \\"echo no | avdmanager create avd --force -n "${avdName}" --abi '${target}/${arch}' --package 'system-images;android-${apiLevel};${target};${arch}'"`); );
}
// start emulator // start emulator
console.log('Starting emulator.'); console.log('Starting emulator.');
@@ -23,7 +31,7 @@ export async function launchEmulator(apiLevel: number, target: string, arch: str
emulatorOptions += ' -accel off'; emulatorOptions += ' -accel off';
} }
await exec.exec(`sh -c \\"${process.env.ANDROID_HOME}/emulator/emulator -avd "${avdName}" ${emulatorOptions} &"`, [], { await exec.exec(`sh -c \\"${process.env.ANDROID_SDK_ROOT}/emulator/emulator -avd "${avdName}" ${emulatorOptions} &"`, [], {
listeners: { listeners: {
stderr: (data: Buffer) => { stderr: (data: Buffer) => {
if (data.toString().includes('invalid command-line parameter')) { if (data.toString().includes('invalid command-line parameter')) {
+5 -1
View File
@@ -39,6 +39,10 @@ async function run() {
const profile = core.getInput('profile'); const profile = core.getInput('profile');
console.log(`Hardware profile: ${profile}`); console.log(`Hardware profile: ${profile}`);
// SD card path or size used for creating the AVD
const sdcardPathOrSize = core.getInput('sdcard-path-or-size');
console.log(`SD card path or size: ${sdcardPathOrSize}`);
// custom name used for creating the AVD // custom name used for creating the AVD
const avdName = core.getInput('avd-name'); const avdName = core.getInput('avd-name');
console.log(`AVD name: ${avdName}`); console.log(`AVD name: ${avdName}`);
@@ -94,7 +98,7 @@ async function run() {
await installAndroidSdk(apiLevel, target, arch, emulatorBuild, ndkVersion, cmakeVersion); await installAndroidSdk(apiLevel, target, arch, emulatorBuild, ndkVersion, cmakeVersion);
// launch an emulator // launch an emulator
await launchEmulator(apiLevel, target, arch, profile, avdName, emulatorOptions, disableAnimations); await launchEmulator(apiLevel, target, arch, profile, sdcardPathOrSize, avdName, emulatorOptions, disableAnimations);
// execute the custom script // execute the custom script
try { try {
+14 -10
View File
@@ -4,7 +4,7 @@ import * as io from '@actions/io';
import * as tc from '@actions/tool-cache'; import * as tc from '@actions/tool-cache';
import * as fs from 'fs'; import * as fs from 'fs';
const BUILD_TOOLS_VERSION = '30.0.2'; const BUILD_TOOLS_VERSION = '30.0.3';
const CMDLINE_TOOLS_URL_MAC = 'https://dl.google.com/android/repository/commandlinetools-mac-6858069_latest.zip'; const CMDLINE_TOOLS_URL_MAC = 'https://dl.google.com/android/repository/commandlinetools-mac-6858069_latest.zip';
const CMDLINE_TOOLS_URL_LINUX = 'https://dl.google.com/android/repository/commandlinetools-linux-6858069_latest.zip'; const CMDLINE_TOOLS_URL_LINUX = 'https://dl.google.com/android/repository/commandlinetools-linux-6858069_latest.zip';
@@ -16,10 +16,10 @@ export async function installAndroidSdk(apiLevel: number, target: string, arch:
const isOnMac = process.platform === 'darwin'; const isOnMac = process.platform === 'darwin';
if (!isOnMac) { if (!isOnMac) {
await exec.exec(`sh -c \\"sudo chown $USER:$USER ${process.env.ANDROID_HOME} -R`); await exec.exec(`sh -c \\"sudo chown $USER:$USER ${process.env.ANDROID_SDK_ROOT} -R`);
} }
const cmdlineToolsPath = `${process.env.ANDROID_HOME}/cmdline-tools`; const cmdlineToolsPath = `${process.env.ANDROID_SDK_ROOT}/cmdline-tools`;
if (!fs.existsSync(cmdlineToolsPath)) { if (!fs.existsSync(cmdlineToolsPath)) {
console.log('Installing new cmdline-tools.'); console.log('Installing new cmdline-tools.');
const sdkUrl = isOnMac ? CMDLINE_TOOLS_URL_MAC : CMDLINE_TOOLS_URL_LINUX; const sdkUrl = isOnMac ? CMDLINE_TOOLS_URL_MAC : CMDLINE_TOOLS_URL_LINUX;
@@ -27,18 +27,18 @@ export async function installAndroidSdk(apiLevel: number, target: string, arch:
await tc.extractZip(downloadPath, cmdlineToolsPath); await tc.extractZip(downloadPath, cmdlineToolsPath);
await io.mv(`${cmdlineToolsPath}/cmdline-tools`, `${cmdlineToolsPath}/latest`); await io.mv(`${cmdlineToolsPath}/cmdline-tools`, `${cmdlineToolsPath}/latest`);
// add paths for commandline-tools and platform-tools // add paths for commandline-tools and platform-tools
core.addPath(`${cmdlineToolsPath}/latest:${cmdlineToolsPath}/latest/bin:${process.env.ANDROID_HOME}/platform-tools`); core.addPath(`${cmdlineToolsPath}/latest:${cmdlineToolsPath}/latest/bin:${process.env.ANDROID_SDK_ROOT}/platform-tools`);
} }
// additional permission and license requirements for Linux // additional permission and license requirements for Linux
const sdkPreviewLicensePath = `${process.env.ANDROID_HOME}/licenses/android-sdk-preview-license`; const sdkPreviewLicensePath = `${process.env.ANDROID_SDK_ROOT}/licenses/android-sdk-preview-license`;
if (!isOnMac && !fs.existsSync(sdkPreviewLicensePath)) { if (!isOnMac && !fs.existsSync(sdkPreviewLicensePath)) {
fs.writeFileSync(sdkPreviewLicensePath, '\n84831b9409646a918e30573bab4c9c91346d8abd'); fs.writeFileSync(sdkPreviewLicensePath, '\n84831b9409646a918e30573bab4c9c91346d8abd');
} }
// license required for API 30 system images // license required for API 30 and non-default API 28 system images
const sdkArmDbtLicensePath = `${process.env.ANDROID_HOME}/licenses/android-sdk-arm-dbt-license`; const sdkArmDbtLicensePath = `${process.env.ANDROID_SDK_ROOT}/licenses/android-sdk-arm-dbt-license`;
if (apiLevel == 30 && !fs.existsSync(sdkArmDbtLicensePath)) { if (requiresArmDbtLicense(apiLevel, target) && !fs.existsSync(sdkArmDbtLicensePath)) {
fs.writeFileSync(sdkArmDbtLicensePath, '\n859f317696f67ef3d7f30a50a5560e7834b43903'); fs.writeFileSync(sdkArmDbtLicensePath, '\n859f317696f67ef3d7f30a50a5560e7834b43903');
} }
@@ -48,8 +48,8 @@ export async function installAndroidSdk(apiLevel: number, target: string, arch:
if (emulatorBuild) { if (emulatorBuild) {
console.log(`Installing emulator build ${emulatorBuild}.`); console.log(`Installing emulator build ${emulatorBuild}.`);
await exec.exec(`curl -fo emulator.zip https://dl.google.com/android/repository/emulator-${isOnMac ? 'darwin' : 'linux'}-${emulatorBuild}.zip`); await exec.exec(`curl -fo emulator.zip https://dl.google.com/android/repository/emulator-${isOnMac ? 'darwin' : 'linux'}-${emulatorBuild}.zip`);
await io.rmRF(`${process.env.ANDROID_HOME}/emulator`); await io.rmRF(`${process.env.ANDROID_SDK_ROOT}/emulator`);
await exec.exec(`unzip -q emulator.zip -d ${process.env.ANDROID_HOME}`); await exec.exec(`unzip -q emulator.zip -d ${process.env.ANDROID_SDK_ROOT}`);
await io.rmRF('emulator.zip'); await io.rmRF('emulator.zip');
} else { } else {
console.log('Installing latest emulator.'); console.log('Installing latest emulator.');
@@ -67,3 +67,7 @@ export async function installAndroidSdk(apiLevel: number, target: string, arch:
await exec.exec(`sh -c \\"sdkmanager --install 'cmake;${cmakeVersion}' > /dev/null"`); await exec.exec(`sh -c \\"sdkmanager --install 'cmake;${cmakeVersion}' > /dev/null"`);
} }
} }
function requiresArmDbtLicense(apiLevel: number, target: string): boolean {
return apiLevel === 30 || (apiLevel === 28 && target !== 'default');
}
+7 -8
View File
@@ -1,10 +1,9 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android { android {
compileSdkVersion 30 compileSdkVersion 30
buildToolsVersion "30.0.2" buildToolsVersion "30.0.3"
defaultConfig { defaultConfig {
applicationId "com.example.testapp" applicationId "com.example.testapp"
@@ -26,11 +25,11 @@ android {
dependencies { dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.core:core-ktx:1.3.0' implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
testImplementation 'junit:junit:4.13' testImplementation 'junit:junit:4.13'
androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
} }
+2 -2
View File
@@ -1,13 +1,13 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
ext.kotlin_version = '1.4.0' ext.kotlin_version = '1.4.21'
repositories { repositories {
google() google()
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:4.0.1' classpath 'com.android.tools.build:gradle:4.1.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
-12
View File
@@ -24,17 +24,5 @@ kapt.include.compile.classpath=false
# Use R8 instead of ProGuard for code shrinking. # Use R8 instead of ProGuard for code shrinking.
android.enableR8.fullMode=true android.enableR8.fullMode=true
# Enable gradle workers
android.enableGradleWorkers=true
# Enable AndroidX # Enable AndroidX
android.useAndroidX=true android.useAndroidX=true
# Enable rudimentary R class namespacing where each library only contains
# references to the resources it declares instead of declarations plus all
# transitive dependency references.
android.namespacedRClass=true
# Only keep the single relevant constructor for types mentioned in XML files
# instead of using a parameter wildcard which keeps them all.
android.useMinimalKeepRules=true
+1 -1
View File
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip