Permalink
Browse files

feat(console): console command exec times

  • Loading branch information...
jkuri committed Sep 5, 2017
1 parent dcffee0 commit 2611e4fe1e129b8fb190bb8829e94cad5c22219f
@@ -27,7 +27,6 @@ before_script:
- sudo /etc/init.d/docker start
- sudo /etc/init.d/xvfb start
- export DISPLAY=:99
- sudo su abstruse

script:
- if [[ "$SCRIPT" ]]; then newgrp docker && npm run-script $SCRIPT; fi
- if [[ "$SCRIPT" ]]; then npm run-script $SCRIPT; fi
@@ -62,7 +62,7 @@ jobEvents
.subscribe(event => {
let msg = [
yellow('['),
blue('abstruse_' + event.build_id + '_' + event.job_id),
cyan('abstruse_' + event.build_id + '_' + event.job_id),
yellow(']'),
' --- ',
yellow(event.data)
@@ -200,7 +200,7 @@ export function startJob(proc: JobProcess): Promise<void> {
} else if (event.type === 'container') {
let msg = [
yellow('['),
blue('abstruse_' + proc.build_id + '_' + proc.job_id),
cyan('abstruse_' + proc.build_id + '_' + proc.job_id),
yellow(']'),
' --- ',
yellow(event.data)
@@ -4,7 +4,7 @@ import * as child_process from 'child_process';
import { generateRandomId } from './utils';
import { getRepositoryByBuildId } from './db/repository';
import { Observable } from 'rxjs';
import { green, red, bold, yellow, blue } from 'chalk';
import { green, red, bold, yellow, blue, cyan } from 'chalk';
import { CommandType, Command } from './config';
import { JobProcess } from './process-manager';
const nodePty = require('node-pty');
@@ -90,52 +90,76 @@ export function startBuildProcess(

function executeInContainer(name: string, cmd: Command): Observable<ProcessOutput> {
return new Observable(observer => {
let executed = false;
let attach = null;
let detachKey = 'D';
let success = false;

attach = nodePty.spawn('docker', ['attach', `--detach-keys=${detachKey}`, name]);

attach.on('data', data => {
if (!executed) {
const exec = `/usr/bin/abstruse '${cmd.command}'\r`;
attach.write(exec);
observer.next({ type: 'data', data: yellow('==> ' + cmd.command) + '\r' });
executed = true;
} else {
if (data.includes('[success]')) {
if (cmd.type === CommandType.script) {
observer.next({ type: 'data', data: green(data.replace(/\n\r/g, '')) });
}

success = true;
attach.write(detachKey);
} else if (data.includes('[error]')) {
attach.kill();
observer.error(red(data.replace(/\n\r/g, '')));
} else if (!data.includes('/usr/bin/abstruse') &&
!data.includes('exit $?') && !data.includes('logout') &&
!data.includes('read escape sequence')) {
observer.next({ type: 'data', data: data.replace('> ', '') });
}
}
});
const startTime = new Date().getTime();
const start = nodePty.spawn('docker', ['start', name], { name: 'xterm-color' });

attach.on('exit', code => {
if (code !== 0 && !success) {
start.on('exit', startCode => {
if (startCode !== 0) {
const msg = [
yellow('['),
blue(name),
yellow(']'),
' --- ',
`Executed command returned exit code ${red(code.toString())}`
yellow('['), cyan(name), yellow(']'), ' --- ',
'Container errored with exit code ' + red(startCode)
].join('');
observer.error(msg);
}

let executed = false;
let success = false;
let dk = null;
let attach = null;

if (cmd.command.includes('init.d') && cmd.command.includes('start')) {
dk = 'Q';
attach = nodePty.spawn('docker', ['attach', `--detach-keys=${dk}`, name]);
} else {
observer.next({ type: 'exit', data: '0' });
observer.complete();
attach = nodePty.spawn('docker', ['exec', `-it`, name, 'bash', '-l']);
}

attach.on('data', data => {
if (!executed) {
const exec = `/usr/bin/abstruse '${cmd.command}'\r`;
attach.write(exec);
observer.next({ type: 'data', data: yellow('==> ' + cmd.command) + '\r' });
executed = true;
} else {
if (data.includes('[success]')) {
if (cmd.type === CommandType.script) {
observer.next({ type: 'data', data: green(data.replace(/\n\r/g, '')) });
}

success = true;
if (dk) {
attach.write(dk);
} else {
attach.write('pkill -9 sleep && exit 100\r');
}
} else if (data.includes('[error]')) {
attach.kill();
observer.error(red(data.replace(/\n\r/g, '')));
} else if (!data.includes('/usr/bin/abstruse') &&
!data.includes('exit 100') && !data.includes('logout') &&
!data.includes('read escape sequence')) {
observer.next({ type: 'data', data: data.replace('> ', '') });
}
}
});

attach.on('exit', code => {
const endTime = new Date().getTime();
const totalTime = endTime - startTime;

if (code !== 0 && !success) {
const msg = [
yellow('['), cyan(name), yellow(']'), ' --- ',
`Executed command returned exit code ${red(code.toString())}`
].join('');
observer.next({ type: 'data', data: `[exectime]: ${totalTime}` });
observer.error(msg);
} else {
observer.next({ type: 'data', data: `[exectime]: ${totalTime}` });
observer.next({ type: 'exit', data: '0' });
observer.complete();
}
});
});
});
}
@@ -1,43 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
x="0px"
y="0px"
viewBox="0 0 100 125"
style="enable-background:new 0 0 100 100;"
xml:space="preserve"
id="svg8"
inkscape:version="0.92.1 r15371"><metadata
id="metadata14"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs12" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2038"
inkscape:window-height="1263"
id="namedview10"
showgrid="false"
inkscape:zoom="5.3400704"
inkscape:cx="46.464526"
inkscape:cy="38.739038"
inkscape:window-x="988"
inkscape:window-y="268"
inkscape:window-maximized="0"
inkscape:current-layer="svg8" /><path
d="M68,52H32v-4h36V52z M82,20v60c0,1.1-0.9,2-2,2H20c-1.1,0-2-0.9-2-2V20c0-1.1,0.9-2,2-2h60C81.1,18,82,18.9,82,20z M78,22 H22v56h56V22z"
id="path2"
style="fill:#FFFFFF" /></svg>
<svg width="62px" height="10px" viewBox="0 0 62 10" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g transform="translate(-19.000000, -1.000000)" fill-rule="nonzero" fill="#FFFFFF">
<g id="Group" transform="translate(19.000000, 0.637820)">
<path d="M5,0.36218 C2.2386,0.36218 0,2.60068 0,5.3622 C0,8.1237 2.2386,10.3622 5,10.3622 L57,10.3622 C59.7614,10.3622 62,8.1237 62,5.3622 C62,2.60068 59.7614,0.36218 57,0.36218 L5,0.36218 Z" id="Shape"></path>
</g>
</g>
</g>
</svg>
@@ -1,43 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
x="0px"
y="0px"
viewBox="0 0 100 125"
style="enable-background:new 0 0 100 100;"
xml:space="preserve"
id="svg8"
inkscape:version="0.92.1 r15371"><metadata
id="metadata14"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs12" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1320"
inkscape:window-height="887"
id="namedview10"
showgrid="false"
inkscape:zoom="2.6700352"
inkscape:cx="89.442291"
inkscape:cy="49.909641"
inkscape:window-x="908"
inkscape:window-y="99"
inkscape:window-maximized="0"
inkscape:current-layer="svg8" /><path
d="M48,52H32v-4h16V32h4v16h16v4H52v16h-4V52z M82,20v60c0,1.1-0.9,2-2,2H20c-1.1,0-2-0.9-2-2V20c0-1.1,0.9-2,2-2h60 C81.1,18,82,18.9,82,20z M78,22H22v56h56V22z"
id="path2"
style="fill:#FFFFFF" /></svg>
<svg width="64px" height="64px" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g transform="translate(-18.000000, -1.000000)" fill-rule="nonzero" fill="#FFFFFF">
<g id="Group" transform="translate(18.000000, 0.637820)">
<path d="M32,0.36219 C29.2385,0.36219 27,2.60079 27,5.36219 L27,27.36216 L5,27.36216 C2.2386,27.36216 0,29.60066 0,32.3622 C0,35.1237 2.2386,37.3622 5,37.3622 L27,37.3622 L27,59.3622 C27,62.1236 29.2385,64.3622 32,64.3622 C34.7615,64.3622 37,62.1236 37,59.3622 L37,37.3622 L59,37.3622 C61.7614,37.3622 64,35.1237 64,32.3622 C64,29.60067 61.7614,27.36216 59,27.36216 L37,27.36216 L37,5.3622 C37,2.6008 34.7615,0.3622 32,0.3622 L32,0.36219 Z" id="Shape"></path>
</g>
</g>
</g>
</svg>
@@ -1,11 +1,12 @@
<div class="window-terminal-container dracula-ansi-theme" *ngIf="!noData">
<div class="terminal" *ngFor="let cmd of commands; let i = index;" [id]="i">
<div class="command-line" (click)="toogleCommand(i)">
<div class="command-line" (click)="toogleCommand(i)" [class.is-opened]="cmd.visible">
<span class="icon">
<img *ngIf="cmd.visible" src="images/icons/collapse.svg">
<img *ngIf="!cmd.visible" src="images/icons/expand.svg">
</span>
<span class="command" [innerHTML]="cmd.command"></span>
<span class="time" *ngIf="cmd.time">{{ cmd.time }}</span>
</div>
<pre class="output" [class.is-hidden]="!cmd.visible" [innerHTML]="cmd.output"></pre>
</div>
@@ -9,7 +9,7 @@ export class AppTerminalComponent implements OnInit {
@Input() data: any;
@Input() options: { size: 'normal' | 'large' };
au: any;
commands: { command: string, visible: boolean, output: string }[];
commands: { command: string, visible: boolean, output: string, time: string }[];
noData: boolean;

constructor(private elementRef: ElementRef) {
@@ -39,12 +39,21 @@ export class AppTerminalComponent implements OnInit {
let commands: string[] = [];

if (output.match(regex)) {
while (match = regex.exec(output)) { commands.push(match[0]); }
while (match = regex.exec(output)) {
commands.push(match[0]);
}

if (commands.length > 1) {
this.commands = [];
}

let retime = new RegExp('exectime.*(\\d)', 'igm');
let times = [];
while (match = retime.exec(output)) {
let t = match[0].replace('exectime]: ', '').replace(/<span.*/, '');
times.push(t);
}

this.commands = commands.reduce((acc, curr, i) => {
let next = commands[i + 1] || '';
next = next.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
@@ -53,15 +62,24 @@ export class AppTerminalComponent implements OnInit {
if (!output.match(re)) {
re = new RegExp('(' + c + ')' + '[\\s\\S]+');
}
let retime = new RegExp('exectime.*(\\d)', 'igm');
let t = times[i] ? parseFloat(<any>(times[i] / 10)).toFixed(0) : null;
let time = t && parseInt(<any>t, 10);

return acc.concat({
command: curr.trim(),
visible: i === commands.length - 1 ? true : false,
output: output.match(re) && output.match(re)[2] ? output.match(re)[2].trim() : ''
output: output.match(re) && output.match(re)[2] ? output.match(re)[2].trim() : '',
time: times[i] ? this.getDuration(time) : null
});
}, this.commands);
} else {
this.commands[this.commands.length - 1].output += output;
if (output.includes('[exectime]')) {
const time = parseInt(output.replace('[exectime]', ''), 10);
console.log(time);
} else {
this.commands[this.commands.length - 1].output += output;
}
}

this.commands = this.commands.map((cmd, i) => {
@@ -84,4 +102,26 @@ export class AppTerminalComponent implements OnInit {
toogleCommand(index: number) {
this.commands[index].visible = !this.commands[index].visible;
}

getDuration(millis: number): string {
const dur = {};
const units = [
{label: 'millis', mod: 100 }, // millis
{label: 'seconds', mod: 60 },
{label: 'minutes', mod: 60 },
{label: 'hours', mod: 24 },
{label: 'days', mod: 31 }
];
units.forEach(u => millis = (millis - (dur[u.label] = (millis % u.mod))) / u.mod);
const nonZero = (u) => { return dur[u.label]; };
dur.toString = () => {
return units
.reverse()
.filter(nonZero)
.map(u => dur[u.label] + ' ' + (dur[u.label] === 1 ? u.label.slice(0, -1) : u.label))
.join(', ');
};

return dur.toString();
}
}
@@ -26,19 +26,19 @@
.command-line
display: flex
align-items: center
background: #000000
margin: 2px 5px
padding: 0px
border-radius: 2px
cursor: pointer
line-height: 10px
position: relative

&.is-opened
background: lighten(#000000, 5)

.icon
margin-top: 6px

img
width: 24px
height: 24px
width: 12px
height: 12px

.command
font-size: 12px
@@ -50,6 +50,14 @@
color: $white
font-weight: $weight-semibold

.time
position: absolute
display: block
font-size: 12px
color: $white
right: 5px
top: 8px

.output
display: inline-block
width: 100%

0 comments on commit 2611e4f

Please sign in to comment.