diff --git a/package-lock.json b/package-lock.json index c6f3005..ea58289 100644 --- a/package-lock.json +++ b/package-lock.json @@ -393,18 +393,6 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, "currently-unhandled": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", @@ -491,14 +479,6 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, - "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "requires": { - "once": "^1.4.0" - } - }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -536,20 +516,6 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, "express": { "version": "4.17.1", "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", @@ -688,14 +654,6 @@ "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", "dev": true }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "requires": { - "pump": "^3.0.0" - } - }, "glob": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", @@ -802,11 +760,6 @@ "number-is-nan": "^1.0.0" } }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" - }, "is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", @@ -822,7 +775,8 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true }, "js-tokens": { "version": "4.0.0", @@ -997,11 +951,6 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" - }, "node-notifier": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.0.tgz", @@ -1027,14 +976,6 @@ "validate-npm-package-license": "^3.0.1" } }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "requires": { - "path-key": "^2.0.0" - } - }, "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", @@ -1064,15 +1005,11 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, "requires": { "wrappy": "1" } }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" - }, "parse-json": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", @@ -1102,11 +1039,6 @@ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" - }, "path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", @@ -1159,15 +1091,6 @@ "ipaddr.js": "1.9.0" } }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "qs": { "version": "6.7.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", @@ -1265,7 +1188,8 @@ "semver": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true }, "send": { "version": "0.17.1", @@ -1325,19 +1249,6 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" - }, "shellwords": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", @@ -1347,7 +1258,8 @@ "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true }, "source-map": { "version": "0.6.1", @@ -1415,11 +1327,6 @@ "is-utf8": "^0.2.0" } }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" - }, "strip-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", @@ -1616,6 +1523,7 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, "requires": { "isexe": "^2.0.0" } @@ -1623,7 +1531,8 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true }, "xtend": { "version": "4.0.1", diff --git a/package.json b/package.json index e0afbc1..22904c9 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,6 @@ "compression": "^1.7.3", "dotenv": "^8.0.0", "dotenv-flow": "^2.0.0", - "execa": "^1.0.0", "express": "^4.16.4", "express-github-webhook": "^1.0.6", "fs-extra": "^6.0.1", diff --git a/src/Server.ts b/src/Server.ts index f478451..ad9d1d0 100644 --- a/src/Server.ts +++ b/src/Server.ts @@ -8,10 +8,9 @@ import { __PROD__, credentials, httpPort, httpsPort, webhookOptions } from 'conf import http from 'http'; import https from 'https'; import { Hierarchy } from 'models'; -import execa from 'execa'; import * as Tracers from 'tracers'; import { errorHandlerMiddleware, frontendMiddleware, redirectMiddleware } from 'middlewares'; -import { pull } from 'utils/misc'; +import { execute, pull } from 'utils/misc'; import { frontendBuildDir, frontendBuiltDir, frontendDir, rootDir } from 'config/paths'; const Webhook = require('express-github-webhook'); @@ -76,13 +75,13 @@ export default class Server { async update(commit?: string) { await pull(rootDir, 'server', commit); - await execa.shell('npm install', {cwd: rootDir}); + await execute('npm install', {cwd: rootDir}); process.exit(0); }; async updateFrontend(commit?: string) { await pull(frontendDir, 'algorithm-visualizer', commit); - await execa.shell([ + await execute([ 'npm install', 'npm run build', `rm -rf ${frontendBuiltDir}`, diff --git a/src/models/Hierarchy.ts b/src/models/Hierarchy.ts index c5d6ff1..ec3c67b 100644 --- a/src/models/Hierarchy.ts +++ b/src/models/Hierarchy.ts @@ -4,8 +4,7 @@ import { GitHubApi } from 'utils/apis'; import { Algorithm, Category, File } from 'models'; import { Author } from 'models/File'; import { algorithmsDir } from 'config/paths'; -import { pull } from 'utils/misc'; -import execa from 'execa'; +import { execute, pull } from 'utils/misc'; type CommitAuthors = { [sha: string]: Author, @@ -52,7 +51,7 @@ export class Hierarchy { async cacheContributors(files: File[], commitAuthors: CommitAuthors) { for (const file of files) { - const stdout = await execa('git', ['--no-pager', 'log', '--follow', '--no-merges', '--format="%H"', '--', `"${file.path}"`], { + const stdout = await execute(`git --no-pager log --follow --no-merges --format="%H" -- "${path.relative(this.path, file.path)}"`, { cwd: this.path, }); const output = stdout.toString().replace(/\n$/, ''); diff --git a/src/tracers/DockerTracer.ts b/src/tracers/DockerTracer.ts index ae40663..b3d5e27 100644 --- a/src/tracers/DockerTracer.ts +++ b/src/tracers/DockerTracer.ts @@ -5,7 +5,7 @@ import uuid from 'uuid'; import fs from 'fs-extra'; import { memoryLimit, timeLimit } from 'config/constants'; import { codesDir } from 'config/paths'; -import execa = require('execa'); +import { execute } from 'utils/misc'; export class DockerTracer extends Tracer { private readonly directory: string; @@ -21,7 +21,7 @@ export class DockerTracer extends Tracer { build(release: Release) { const {tag_name} = release; - return execa('docker', ['build', '-t', this.imageName, '.', '--build-arg', `tag_name=${tag_name}`], {cwd: this.directory}); + return execute(`docker build -t ${this.imageName} . --build-arg tag_name=${tag_name}`, {cwd: this.directory}); } route(router: express.Router) { @@ -33,19 +33,19 @@ export class DockerTracer extends Tracer { const containerName = uuid.v4(); let killed = false; const timer = setTimeout(() => { - execa('docker', ['kill', containerName]).then(() => { + execute(`docker kill ${containerName}`).then(() => { killed = true; }); }, timeLimit); - return execa('docker', [ - 'run', '--rm', + return execute([ + 'docker run --rm', `--name=${containerName}`, '-w=/usr/visualization', `-v=${tempPath}:/usr/visualization:rw`, `-m=${memoryLimit}m`, - '-e', 'ALGORITHM_VISUALIZER=1', + '-e ALGORITHM_VISUALIZER=1', this.imageName, - ]).catch(error => { + ].join(' ')).catch(error => { if (killed) throw new Error('Time Limit Exceeded'); throw error; }).finally(() => clearTimeout(timer)); diff --git a/src/utils/misc.ts b/src/utils/misc.ts index ed4303e..6074ad2 100644 --- a/src/utils/misc.ts +++ b/src/utils/misc.ts @@ -1,8 +1,9 @@ import axios from 'axios'; import fs from 'fs-extra'; -import execa from 'execa'; import { File } from 'models'; import removeMarkdown from 'remove-markdown'; +import * as child_process from 'child_process'; +import { ExecOptions } from 'child_process'; export function download(url: string, localPath: string) { return axios({url, method: 'GET', responseType: 'stream'}) @@ -16,11 +17,11 @@ export function download(url: string, localPath: string) { export async function pull(dir: string, repo: string, commit = 'origin/master') { if (fs.pathExistsSync(dir)) { - await execa.shell(`git fetch`, {cwd: dir}); + await execute(`git fetch`, {cwd: dir}); } else { - await execa.shell(`git clone https://github.com/algorithm-visualizer/${repo}.git ${dir}`); + await execute(`git clone https://github.com/algorithm-visualizer/${repo}.git ${dir}`); } - await execa.shell(`git reset --hard ${commit}`, {cwd: dir}); + await execute(`git reset --hard ${commit}`, {cwd: dir}); } export function getDescription(files: File[]) { @@ -34,6 +35,18 @@ export function getDescription(files: File[]) { return removeMarkdown(descriptionLines.join(' ')); } -export function rethrow(err: any) { - throw err; +type ExecuteOptions = ExecOptions & { + stdout?: NodeJS.WriteStream; + stderr?: NodeJS.WriteStream; +} + +export function execute(command: string, {stdout, stderr, ...options}: ExecuteOptions = {}): Promise { + return new Promise((resolve, reject) => { + const child = child_process.exec(command, options, (error, stdout, stderr) => { + if (error) return reject(error.code ? new Error(stderr) : error); + resolve(stdout); + }); + if (child.stdout && stdout) child.stdout.pipe(stdout); + if (child.stderr && stderr) child.stderr.pipe(stderr); + }); } diff --git a/tsconfig.json b/tsconfig.json index eaedd03..6d83e6f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,5 +18,6 @@ }, "exclude": [ "node_modules", + "public" ] }