Skip to content

Instantly share code, notes, and snippets.

@chapko
Last active March 3, 2017 11:03
Show Gist options
  • Save chapko/521a3ee7355225cc14ea5c551da566c4 to your computer and use it in GitHub Desktop.
Save chapko/521a3ee7355225cc14ea5c551da566c4 to your computer and use it in GitHub Desktop.
#!/bin/bash
apk="$1"
out="$2"
apksigner sign \
--ks="$HOME/.android/debug-key.keystore" \
--ks-pass "pass:12345678" \
--out="$out" \
"$apk"
apksigner verify "$out"
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const cp = require('child_process');
const spawn = cp.spawn;
const spawnSync = cp.spawnSync;
const getConnectedDevices = require('./devices').getConnectedDevices;
function createDeferredObject() {
let resolve, reject, promise;
promise = new Promise((_resolve, _reject) => {
resolve = _resolve;
reject = _reject;
});
return { promise, resolve, reject };
}
function logHeader(device) {
return (
`Android device log.
${(new Date()).toISOString()}
Device info:
${JSON.stringify(device, null, 4)}
================================================================================
`);
}
const SIGINT_LOG = `
--------------
Got SIGINT
--------------
`;
function logger(file) {
let finished = false;
const stream = fs.createWriteStream(file);
stream.on('finish', () => finished = true);
stream.on('error', (err) => {
console.log('Logging error:', err.message);
console.log(err.stack);
stream.end();
});
return {
stream: stream,
write(text) {
if (finished) {
return Promise.resolve();
}
return new Promise((resolve, reject) => {
stream.write(text, 'utf8', function (err) {
if (err) {
reject(err);
}
resolve();
});
});
},
end(text) {
if (finished) {
return;
}
stream.end(text, 'utf8');
}
};
}
function createServerJob(devices, serverLogFile, serverLog) {
const testServer = path.join(
__dirname,
'../Thali_CordovaPlugin/test/TestServer/index.js'
);
const options = JSON.stringify({
devices: {
android: devices.length
}
});
const child = spawn('jx', [testServer, options]);
child.stdout.pipe(serverLog.stream);
child.stdout.pipe(process.stdout);
child.stderr.pipe(process.stderr);
return {
child: child,
logFile: serverLogFile,
log: serverLog,
};
}
function run() {
const devices = getConnectedDevices();
console.log(`Detected ${devices.length} devices`);
const logsDir = path.join(__dirname, (new Date()).toISOString());
fs.mkdirSync(logsDir);
const jobs = devices.map((device) => {
// clear logcat cache
const logFile = path.join(logsDir, `${device.model} (${device.id}).log`);
const log = logger(logFile);
log.write(logHeader(device));
spawnSync('adb', [
'-s', device.id,
'logcat -c',
]);
const child = spawn('adb', [
'-s', device.id,
'logcat',
'-v', 'threadtime',
'jxcore-log:V', '*:E'
]);
child.stdout.pipe(log.stream);
function onExit(code, signal) {
if (code !== null) {
log.end(`Exited with ${code} code`);
} else {
log.end(`Terminated with ${signal} signal`);
}
}
child.on('error', (err) => {
log.write(`Unexpected Error: ${err.message}`);
log.end(err.stack);
});
child.on('close', onExit);
child.on('exit', onExit);
return { child, logFile, log };
});
const serverLogFile = path.join(logsDir, 'server.log');
const serverLog = logger(serverLogFile);
const serverJob = createServerJob(devices, serverLogFile, serverLog);
jobs.push(serverJob);
const files = jobs.map(j => j.logFile);
console.log(`See logs here:\n ${files.join('\n ')}`);
process.on('SIGINT', () => {
const files = jobs.map((j) => j.logFile);
Promise.all(jobs.map((j) => j.log.write(SIGINT_LOG)))
.then(() => {
exit(jobs, 'SIGINT');
})
.catch((err) => {
console.error('ERROR:', err.message);
console.error(err.stack);
process.exit(1);
});
});
serverJob.child.on('close', () => {
exit(jobs);
});
}
function exit(jobs, signal) {
console.log('\nKilling adb processes...');
jobs.forEach((j) => {
j.child.kill(signal);
});
console.log('Done');
process.exit(0);
}
run();
const spawnSync = require('child_process').spawnSync;
function parseAdbDevice(rawString) {
if (rawString.startsWith('List of')) {
return null;
}
if (rawString.startsWith('*')) {
return null;
}
const [id, type, ...features] = rawString.split(/\s+/g);
if (type !== 'device') {
return null;
}
const device = { id, type };
features.forEach((rawFeature) => {
const [key, value] = rawFeature.split(':');
device[key] = value;
});
return device;
}
function getConnectedDevices() {
const adb = spawnSync('adb', ['devices', '-l']);
const devices = adb.stdout.toString()
.split('\n')
.map(s => s.trim())
.filter((row, index) => (row.length && index > 0))
.map(parseAdbDevice)
.filter(device => (device.type === 'device'));
return devices;
}
module.exports = {
parseAdbDevice,
getConnectedDevices,
};
#!/usr/bin/env node
const path = require('path');
const spawn = require('child_process').spawn;
const getConnectedDevices = require('./devices').getConnectedDevices;
function run(apk) {
const devices = getConnectedDevices();
console.log(`Installing apk on\n ${devices.map(d=>d.id).join('\n ')}`);
const children = devices.map(({ id }) => {
return spawn('adb', [
'-s', id,
'install', '-r', apk,
]);
});
children.forEach(({ stderr, stdout }) => {
stderr.pipe(process.stderr);
stdout.pipe(process.stdout);
});
}
if (!process.argv[2]) {
console.error('APK file path is required');
process.exit(1);
}
var apk = path.join(process.cwd(), process.argv[2]);
run(apk);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment