mirror of
https://github.com/aspnet/JavaScriptServices.git
synced 2025-12-22 17:47:53 +00:00
124 lines
4.8 KiB
TypeScript
124 lines
4.8 KiB
TypeScript
import * as childProcess from 'child_process';
|
|
import * as path from 'path';
|
|
import * as readline from 'readline';
|
|
import { waitUntilPortState } from './ports';
|
|
const treeKill = require('tree-kill');
|
|
const crossSpawn: typeof childProcess.spawn = require('cross-spawn');
|
|
const defaultPort = 5000;
|
|
const defaultInterface = 'localhost';
|
|
|
|
export const defaultUrl = `http://localhost:${ defaultPort }`;
|
|
|
|
export enum AspNetCoreEnviroment {
|
|
development,
|
|
production
|
|
}
|
|
|
|
export class AspNetProcess {
|
|
public static RunInMochaContext(cwd: string, mode: AspNetCoreEnviroment, dllToRun?: string) {
|
|
// Set up mocha before/after callbacks so that a 'dotnet run' process exists
|
|
// for the same duration as the context this is called inside
|
|
let aspNetProcess: AspNetProcess;
|
|
before(() => {
|
|
aspNetProcess = new AspNetProcess(cwd, mode, dllToRun);
|
|
return aspNetProcess.waitUntilListening();
|
|
});
|
|
after(() => aspNetProcess.dispose());
|
|
}
|
|
|
|
private _process: childProcess.ChildProcess;
|
|
private _processHasExited: boolean;
|
|
private _stdoutReader: readline.ReadLine;
|
|
|
|
constructor(cwd: string, mode: AspNetCoreEnviroment, dllToRun?: string) {
|
|
try {
|
|
// Prepare env for child process. Note that it doesn't inherit parent's env vars automatically,
|
|
// hence cloning process.env.
|
|
const childProcessEnv = Object.assign({}, process.env);
|
|
childProcessEnv.ASPNETCORE_ENVIRONMENT = mode === AspNetCoreEnviroment.development ? 'Development' : 'Production';
|
|
|
|
const verbOrAssembly = dllToRun || 'run';
|
|
console.log(`Running 'dotnet ${ verbOrAssembly }' in ${ cwd }`);
|
|
this._process = crossSpawn('dotnet', [verbOrAssembly], { cwd: cwd, stdio: 'pipe', env: childProcessEnv });
|
|
this._stdoutReader = readline.createInterface(this._process.stdout, null);
|
|
|
|
// Echo stdout to the test process's own stdout
|
|
this._stdoutReader.on('line', line => {
|
|
console.log(`[dotnet] ${ line.toString() }`);
|
|
});
|
|
|
|
// Also echo stderr
|
|
this._process.stderr.on('data', chunk => {
|
|
console.log(`[dotnet ERROR] ${ chunk.toString() }`);
|
|
});
|
|
|
|
// Ensure the process isn't orphaned even if Node crashes before we're disposed
|
|
process.on('exit', () => this._killAspNetProcess());
|
|
|
|
// Also track whether it exited on its own already
|
|
this._process.on('exit', () => {
|
|
this._processHasExited = true;
|
|
});
|
|
} catch(ex) {
|
|
console.log('ERROR: ' + ex.toString());
|
|
throw ex;
|
|
}
|
|
}
|
|
|
|
public waitUntilListening(): Promise<any> {
|
|
return new Promise((resolve, reject) => {
|
|
this._stdoutReader.on('line', (line: string) => {
|
|
if (line.startsWith('Now listening on:')) {
|
|
resolve();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
public dispose(): Promise<any> {
|
|
return new Promise((resolve, reject) => {
|
|
this._killAspNetProcess(err => {
|
|
if (err) {
|
|
reject(err);
|
|
} else {
|
|
resolve();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
private _killAspNetProcess(callback?: (err: any) => void) {
|
|
callback = callback || (() => {});
|
|
if (!this._processHasExited) {
|
|
// It's important to kill the whole tree, because 'dotnet run' launches a separate 'dotnet exec'
|
|
// child process that would otherwise be left running
|
|
treeKill(this._process.pid, 'SIGINT', err => {
|
|
if (err) {
|
|
callback(err);
|
|
} else {
|
|
// It's not enough just to send a SIGINT to ASP.NET. It will stay open for a moment, completing
|
|
// any outstanding requests. We have to wait for it really to be gone before continuing, otherwise
|
|
// the next test might be unable to start because of the port still being in use.
|
|
console.log(`Waiting until port ${ defaultPort } is closed...`);
|
|
waitUntilPortState(defaultPort, defaultInterface, /* isListening */ false, /* timeoutMs */ 15000, err => {
|
|
if (err) {
|
|
callback(err);
|
|
} else {
|
|
console.log(`Port ${ defaultPort } is now closed`);
|
|
callback(null);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
export function publishProjectSync(sourceDir: string, outputDir: string) {
|
|
childProcess.execSync(`dotnet publish -c Release -o ${ outputDir }`, {
|
|
cwd: sourceDir,
|
|
stdio: 'inherit',
|
|
encoding: 'utf8'
|
|
});
|
|
}
|