Skip to content

Commit

Permalink
feat: Add support of py-ios-device for certificate install on real de…
Browse files Browse the repository at this point in the history
…vices (#1369)
  • Loading branch information
mykola-mokhnach authored Jan 18, 2022
1 parent 6614d50 commit de7ab1e
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 0 deletions.
10 changes: 10 additions & 0 deletions lib/commands/certificate.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {};

Expand Down Expand Up @@ -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();
Expand Down
3 changes: 3 additions & 0 deletions lib/driver.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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) {
Expand Down
96 changes: 96 additions & 0 deletions lib/py-ios-device-client.js
Original file line number Diff line number Diff line change
@@ -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;

0 comments on commit de7ab1e

Please sign in to comment.