Last active
July 22, 2019 10:50
-
-
Save FireyFly/18621625dc8d8f6ecf6ddb8e55002c63 to your computer and use it in GitHub Desktop.
CLI Utilities
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Stuff for finding out which docker containers are autostarting, and making them not do so | |
# https://stackoverflow.com/a/45022623/1267058 | |
# Find containers whose RestartPolicy is 'always'--these are the culprits | |
docker ps -a --format '{{.ID}} {{.Names}}' \ | |
| while read id name; do | |
policy=$(docker inspect $id | jq -r '.[].HostConfig.RestartPolicy.Name') | |
printf '%s %-30s %s\n' "$id" "$name" "$policy" | |
done \ | |
| grep 'always$' | |
# Disable restart for a container | |
docker update --restart=no <ID> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
DirName="$(basename "$PWD" | tr '[:upper:]' '[:lower:]')" | |
ServiceName="$1" | |
ContainerName="$(docker ps --format '{{.Names}}' | grep "${DirName}_${ServiceName}" | head -n 1)" | |
if [ $# == 0 ]; then | |
echo >&2 'usage: dsh <service name> [...command]' | |
exit 1 | |
fi | |
if [ -z "$ContainerName" ]; then | |
echo >&2 "error: no container "${DirName}_${ServiceName}" found" | |
exit 1 | |
fi | |
shift 1 | |
case $# in | |
0) docker exec -ti "$ContainerName" /bin/bash ;; | |
*) docker exec -ti "$ContainerName" "$@" ;; | |
esac |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env node | |
const util = require('util'); | |
const http = require('http'); | |
const { Transform } = require('stream'); | |
const { spawn } = require('child_process'); | |
function findJSONEnd(str, i) { | |
if (str[i] !== '{') throw new Error(`findJSONEnd: Expected '{', found '${str[i]}'`); | |
let j = i + 1, count = 1, mode = 'object'; | |
while (count > 0 && j < str.length) { | |
if (mode === 'object') { | |
if (str[j] === '{') count += 1; | |
else if (str[j] === '}') count -= 1; | |
else if (str[j] === '"') mode = 'string'; | |
} else if (mode === 'string') { | |
if (str[j] === '\\') j += 1; // skip escaped char | |
else if (str[j] === '"') mode = 'object'; | |
} | |
j += 1; | |
} | |
if (j === str.length) return null; | |
return j; | |
} | |
function createJSONObjectArrayTransform() { | |
let hasReceivedFirst = false; | |
let residue = ''; | |
return new Transform({ | |
readableObjectMode: true, | |
readableHighWaterMark: 1, | |
// TODO: implement _flush, test properly | |
transform(chunk, encoding, cb) { | |
let str = residue + chunk; | |
if (!hasReceivedFirst) { | |
// TODO: clean this up a bit | |
while (str.startsWith('Filtering the log data')) { | |
str = str.replace(/^[^\n]+\n/, ''); | |
} | |
if (str.length === 0) return cb(); | |
if (str[0] !== '[') return cb(new Error("JSONObjectArrayTransform: expected stream to start with '['")); | |
str = str.slice(1); | |
hasReceivedFirst = true; | |
} | |
let i = 0; | |
while (i < str.length) { | |
const j = findJSONEnd(str, i); | |
if (j == null) break; | |
this.push(JSON.parse(str.slice(i, j))); | |
// TODO: check for ',' | |
i = j + 1; | |
} | |
residue = str.slice(i); | |
cb(); | |
}, | |
}); | |
} | |
const parseErrorObject = (str) => { | |
const matches = str.trim().match(/^\{\s*\[(.*)\]\n *line: *(\d+),\n *column: *(\d+),\n *sourceURL: *'(.*)'\s*}$/); | |
if (matches == null) return null; | |
const [_, message, line, column, sourceURL] = matches; | |
return { | |
message, | |
line: Number(line), | |
column: Number(column), | |
sourceURL, | |
}; | |
}; | |
const parseFormatStringArgs = (str) => { | |
if (!/^'.*', '.*(?:'|<…>)$/.test(str)) return null; | |
const table = { "'": '"', "\\'": "'", '"': '\\"', '<…>': '"' }; | |
try { | |
return JSON.parse(`[${str.replace(/\\'|'|"|<…>/g, which => table[which])}]`); | |
} catch (err) { | |
return null; | |
} | |
}; | |
const requestToPromise = req => new Promise((resolve, reject) => { | |
req.on('response', (res) => { | |
const chunks = []; | |
res.on('data', (chunk) => { chunks.push(chunk); }); | |
res.on('end', () => resolve(chunks.join(''))); | |
res.on('error', reject); | |
}); | |
}); | |
let fetchLines; | |
{ | |
let cache = {}; | |
fetchLines = async (sourceURL) => { | |
if (cache.url === sourceURL) return cache.lines; | |
const content = await requestToPromise(http.get(sourceURL)); | |
cache.url = sourceURL; | |
cache.lines = content.split('\n'); | |
return cache.lines; | |
}; | |
} | |
const asyncLogger = { | |
promise: Promise.resolve(), | |
logErrorObject({ timestamp, source }, { message, line, column, sourceURL }) { | |
const lineIdx = line - 1; | |
const logSourceLine = (lineno, str) => { | |
const spaces = Array(8 - String(lineno).length).join(' '); | |
console.log(util.format('\x1B[36m%s\x1B[m %s', spaces + lineno, str)); | |
}; | |
this.promise = this.promise | |
.then(() => fetchLines(sourceURL)) | |
.catch((err) => console.error(err)) | |
.then((lines) => { | |
console.log(util.format('\x1B[33m%s\x1B[m \x1B[32m%s\x1B[m', timestamp, source.symbol)); | |
console.log(' ' + message); | |
const lineBefore = lines[lineIdx].slice(0, column - 1); | |
const lineAt = lines[lineIdx][column - 1]; | |
const lineAfter = lines[lineIdx].slice(column); | |
logSourceLine(line - 2, util.format('\x1B[38;5;241m%s\x1B[m', lines[lineIdx - 2])); | |
logSourceLine(line - 1, util.format('\x1B[38;5;241m%s\x1B[m', lines[lineIdx - 1])); | |
logSourceLine(line + 0, util.format('%s\x1B[7m%s\x1B[m%s', lineBefore, lineAt, lineAfter)); | |
logSourceLine(line + 1, util.format('\x1B[38;5;241m%s\x1B[m', lines[lineIdx + 1])); | |
logSourceLine(line + 2, util.format('\x1B[38;5;241m%s\x1B[m', lines[lineIdx + 2])); | |
}); | |
}, | |
logMessage({ timestamp, source }, message) { | |
this.promise = this.promise.then(() => { | |
console.log(util.format('\x1B[33m%s\x1B[m \x1B[32m%s\x1B[m', timestamp, source.symbol)); | |
console.log(message); | |
}); | |
}, | |
}; | |
/* | |
{ | |
traceID, | |
eventMessage, | |
eventType, | |
source, // { symbol, line, image, file } | |
formatString, | |
activityIdentifier, | |
subsystem, | |
category, | |
threadID, | |
senderImageUUID, | |
backtrace, // { frames: [ ... ] } | |
processImagePath, | |
senderImagePath, | |
timestamp, | |
machTimestamp, | |
messageType, | |
processImageUUID, | |
processID, | |
senderProgramCounter, | |
parentActivityIdentifier, | |
timezoneName, | |
} | |
*/ | |
const indent = (str, n) => { | |
const spaces = Array(n + 1).join(' '); | |
return str.split('\n').map(line => spaces + line).join('\n'); | |
} | |
const trimStacktrace = (str) => { | |
const lines = str.split('\n'); | |
return lines.filter(line => !/^ +in /.test(line)).join('\n'); | |
}; | |
spawn('xcrun', ['simctl', 'spawn', 'booted', 'log', 'stream', '--source', '--style=json', '--type=log']) | |
.stdout.pipe(createJSONObjectArrayTransform()) | |
.on('data', (event) => { | |
const { source, eventMessage } = event; | |
if (source != null && source.symbol === 'RCTDefaultLogFunction_block_invoke') { | |
const errorObject = parseErrorObject(eventMessage); | |
const formatStringArgs = parseFormatStringArgs(eventMessage); | |
if (errorObject != null) { | |
asyncLogger.logErrorObject(event, errorObject); | |
} else if (formatStringArgs != null) { | |
const message = indent(trimStacktrace(util.format(...formatStringArgs)), 2); | |
asyncLogger.logMessage(event, message); | |
} else { | |
const message = indent(trimStacktrace(eventMessage), 2); | |
asyncLogger.logMessage(event, message); | |
} | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment