diff --git a/lib/commands/certificate.js b/lib/commands/certificate.js index 0e51ee505..a780cfe7b 100644 --- a/lib/commands/certificate.js +++ b/lib/commands/certificate.js @@ -8,6 +8,7 @@ import path from 'path'; import http from 'http'; import { exec } from 'teen_process'; import { findAPortNotInUse, checkPortStatus } from 'portscanner'; +import Pyidevice from '../py-ios-device-client'; let extensions = {}, commands = {}; @@ -309,6 +310,15 @@ commands.mobileInstallCertificate = async function mobileInstallCertificate (opt log.info(`The certificate cannot be installed via CLI. ` + `Falling back to UI-based deployment`); } + } else { + const client = new Pyidevice(this.opts.udid); + if (await client.assertExists(false)) { + await client.installProfile({payload: Buffer.from(content, 'base64')}); + return; + } else { + log.info('pyidevice is not installed on your system. ' + + 'Falling back to the (slow) UI-based installation'); + } } const tmpRoot = await tempDir.openDir(); diff --git a/lib/driver.js b/lib/driver.js index d7a763bc5..9db23aca7 100644 --- a/lib/driver.js +++ b/lib/driver.js @@ -35,6 +35,7 @@ import AsyncLock from 'async-lock'; import path from 'path'; import IDB from 'appium-idb'; import DEVICE_CONNECTIONS_FACTORY from './device-connections-factory'; +import Pyidevice from './py-ios-device-client'; const SHUTDOWN_OTHER_FEAT_NAME = 'shutdown_other_sims'; @@ -468,6 +469,8 @@ class XCUITestDriver extends BaseDriver { // Retry log capture if Simulator was not running before await startLogCapture(); } + } else if (this.opts.customSSLCert) { + await new Pyidevice(udid).installProfile({payload: this.opts.customSSLCert}); } if (this.opts.app) { diff --git a/lib/py-ios-device-client.js b/lib/py-ios-device-client.js new file mode 100644 index 000000000..1902b473c --- /dev/null +++ b/lib/py-ios-device-client.js @@ -0,0 +1,96 @@ +import { exec } from 'teen_process'; +import { fs, util, tempDir } from 'appium-support'; +import log from './logger'; +import path from 'path'; + +// https://github.com/YueChen-C/py-ios-device + +const BINARY_NAME = 'pyidevice'; + +class Pyidevice { + constructor (udid) { + this.udid = udid; + this.binaryPath = null; + } + + async assertExists (isStrict = true) { + if (this.binaryPath) { + return true; + } + + try { + this.binaryPath = await fs.which(BINARY_NAME); + return true; + } catch (e) { + if (isStrict) { + throw new Error(`${BINARY_NAME} binary cannot be found in PATH. ` + + `Please make sure it is installed. Visit https://github.com/YueChen-C/py-ios-device for ` + + `more details.`); + } + return false; + } + } + + async execute (args, opts = {}) { + await this.assertExists(); + const { + format = 'json', + logStdout = false, + } = opts; + + const finalArgs = [...args, '--udid', this.udid]; + if (format) { + finalArgs.push('--format', format); + } + const cmdStr = util.quote([this.binaryPath, ...finalArgs]); + log.debug(`Executing ${cmdStr}`); + try { + const result = await exec(this.binaryPath, finalArgs); + if (logStdout) { + log.debug(`Command output: ${result.stdout}`); + } + return result; + } catch (e) { + throw new Error(`'${cmdStr}' failed. Original error: ${e.stderr || e.stdout || e.message}`); + } + } + + async listProfiles () { + const {stdout} = await this.execute(['profiles', 'list']); + return JSON.parse(stdout); + } + + async installProfile (opts = {}) { + const { + profilePath, + payload, + } = opts; + if (!profilePath && !payload) { + throw new TypeError('Profile must be defined'); + } + + if (profilePath) { + await this.execute(['profiles', 'install', '--path', profilePath], { + logStdout: true + }); + } else { + const tmpRoot = await tempDir.openDir(); + const tmpProfilePath = path.join(tmpRoot, 'cert.pem'); + try { + await fs.writeFile(tmpProfilePath, payload, 'utf8'); + await this.execute(['profiles', 'install', '--path', tmpProfilePath], { + logStdout: true + }); + } finally { + await fs.rimraf(tmpRoot); + } + } + } + + async removeProfile (name) { + await this.execute(['profiles', 'remove', name], {logStdout: true}); + } +} + +export { Pyidevice }; +export default Pyidevice;