Add feature to copy to publish directory (#53)
* Fix unix run header No space is allowed between the # and the ! * Fix output option * Remove debug log * Fix function JSON fixup * remove package-lock * Fix lint warnings * Roll back unrequired change to function JSON * only change path sep if win * Add copy to output feature * Add e2e tests * rename old tests to perf
This commit is contained in:
Родитель
a27e9ce458
Коммит
987079737d
|
@ -94,6 +94,27 @@
|
|||
"stopOnEntry": false,
|
||||
"type": "node"
|
||||
},
|
||||
{
|
||||
"args": [
|
||||
"pack","-c","./sample2"
|
||||
],
|
||||
"cwd": "${workspaceRoot}",
|
||||
"env": {
|
||||
},
|
||||
"name": "Pack (Copy to Output)",
|
||||
"outFiles": [
|
||||
"${workspaceRoot}/lib/**"
|
||||
],
|
||||
"program": "${workspaceRoot}/src/main.ts",
|
||||
"request": "launch",
|
||||
"runtimeArgs": [
|
||||
"--nolazy"
|
||||
],
|
||||
"runtimeExecutable": null,
|
||||
"sourceMaps": true,
|
||||
"stopOnEntry": false,
|
||||
"type": "node"
|
||||
},
|
||||
{
|
||||
"args": [
|
||||
"pack","./sample2","-u"
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
13
package.json
13
package.json
|
@ -22,9 +22,9 @@
|
|||
"typings": "lib/index.d.ts",
|
||||
"scripts": {
|
||||
"clean": "rimraf lib",
|
||||
"lint": "tslint --force --format verbose \"src/**/*.ts\"",
|
||||
"lint": "tslint -c tslint.json 'src/**/*.ts'",
|
||||
"build": "npm run clean && npm run lint && echo Using TypeScript && tsc --version && tsc --pretty",
|
||||
"test": "npm run build && mocha --compilers ts:ts-node/register --recursive test/**/*-spec.ts",
|
||||
"test": "npm run build && mocha --compilers ts:ts-node/register --recursive test/**/*.test.ts",
|
||||
"watch": "npm run build -- --watch",
|
||||
"watch:test": "npm run test -- --watch",
|
||||
"e2etst": "npm run "
|
||||
|
@ -32,6 +32,8 @@
|
|||
"dependencies": {
|
||||
"commander": "~2.9.0",
|
||||
"debug": "~2.6.1",
|
||||
"mkdirp": "^0.5.1",
|
||||
"ncp": "^2.0.0",
|
||||
"rimraf": "~2.5.4",
|
||||
"webpack": "~3.5.6",
|
||||
"winston": "~2.3.1"
|
||||
|
@ -40,15 +42,20 @@
|
|||
"@types/chai": "3.5.0",
|
||||
"@types/commander": "~2.3.31",
|
||||
"@types/debug": "0.0.29",
|
||||
"@types/mkdirp": "^0.5.1",
|
||||
"@types/mocha": "2.2.41",
|
||||
"@types/ncp": "^2.0.1",
|
||||
"@types/node": "6.0.31",
|
||||
"@types/rimraf": "0.0.28",
|
||||
"@types/supertest": "^2.0.3",
|
||||
"@types/webpack": "~3.0.0",
|
||||
"@types/winston": "~2.2.0",
|
||||
"chai": "~3.5.0",
|
||||
"mocha": "~3.0.0",
|
||||
"ps-node": "^0.1.6",
|
||||
"supertest": "^3.0.0",
|
||||
"ts-node": "~1.0.0",
|
||||
"tslint": "~4.0.0",
|
||||
"tslint": "~5.7.0",
|
||||
"typescript": "~2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
module.exports = function (context, req) {
|
||||
context.log('"./lib/externalScriptFile" function called');
|
||||
const res = {
|
||||
body: {
|
||||
"success":true
|
||||
}
|
||||
}
|
||||
context.done(null, res);
|
||||
};
|
|
@ -1,18 +0,0 @@
|
|||
let sql = require('tedious');
|
||||
|
||||
class Model {
|
||||
getAll() {
|
||||
const request = new sql.Request("select 'hello'", function(err, rowCount) {
|
||||
// no op
|
||||
});
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
add() {
|
||||
return Promise.resolve({});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Model: Model
|
||||
}
|
31
src/main.ts
31
src/main.ts
|
@ -1,4 +1,4 @@
|
|||
#! /usr/bin/env node
|
||||
#!/usr/bin/env node
|
||||
|
||||
import * as program from "commander";
|
||||
import * as path from "path";
|
||||
|
@ -21,6 +21,7 @@ async function runCli() {
|
|||
.description("Will pack the specified path or the current directory if none is specified")
|
||||
.option("-u, --uglify", "Uglify the project when webpacking")
|
||||
.option("-o, --output <path>", "Path for output directory")
|
||||
.option("-c, --copyToOutput", "Copy files to output directory")
|
||||
.action(pack);
|
||||
|
||||
p.command("*", null, { noHelp: true, isDefault: true })
|
||||
|
@ -53,12 +54,12 @@ async function unpack(name: string, options: any) {
|
|||
|
||||
let outputPath = ".funcpack";
|
||||
try {
|
||||
if (options.path) {
|
||||
outputPath = program.opts().path;
|
||||
if (options.output) {
|
||||
outputPath = path.join(options.output, outputPath);
|
||||
}
|
||||
} catch (e) {
|
||||
winston.error(e);
|
||||
throw new Error("Could not parse the uglify option");
|
||||
throw new Error("Could not parse the output option");
|
||||
}
|
||||
|
||||
winston.info("Unpacking project at: " + projectRootPath);
|
||||
|
@ -100,16 +101,28 @@ async function pack(name: string, options: any) {
|
|||
|
||||
let outputPath = ".funcpack";
|
||||
try {
|
||||
if (options.path) {
|
||||
outputPath = program.opts().path;
|
||||
if (options.output) {
|
||||
outputPath = path.join(options.output, outputPath);
|
||||
}
|
||||
} catch (e) {
|
||||
winston.error(e);
|
||||
throw new Error("Could not parse the uglify option");
|
||||
throw new Error("Could not parse the output option");
|
||||
}
|
||||
|
||||
let copyToOutput = false;
|
||||
try {
|
||||
if (options.copyToOutput) {
|
||||
copyToOutput = true;
|
||||
}
|
||||
} catch (e) {
|
||||
winston.error(e);
|
||||
throw new Error("Could not parse the copyToOutput option");
|
||||
}
|
||||
|
||||
// Create new generator object with settings
|
||||
const generator = new PackhostGenerator({
|
||||
copyToOutput,
|
||||
outputPath,
|
||||
projectRootPath,
|
||||
});
|
||||
|
||||
|
@ -126,10 +139,10 @@ async function pack(name: string, options: any) {
|
|||
try {
|
||||
winston.info("Webpacking project");
|
||||
await WebpackRunner.run({
|
||||
ignoredModules: config.ignoredModules,
|
||||
outputPath,
|
||||
projectRootPath,
|
||||
uglify,
|
||||
outputPath,
|
||||
ignoredModules: config.ignoredModules,
|
||||
});
|
||||
} catch (error) {
|
||||
winston.error(error);
|
||||
|
|
|
@ -15,6 +15,7 @@ export class PackhostGenerator {
|
|||
this.options = options;
|
||||
this.options.indexFileName = this.options.indexFileName || "index.js";
|
||||
this.options.outputPath = this.options.outputPath || ".funcpack";
|
||||
this.options.copyToOutput = this.options.copyToOutput || false;
|
||||
debug("Created new PackhostGenerator for project at: %s", this.options.projectRootPath);
|
||||
}
|
||||
|
||||
|
@ -85,13 +86,13 @@ export class PackhostGenerator {
|
|||
directory: dir,
|
||||
});
|
||||
return null;
|
||||
//throw new Error(`Function ${name} does not have a valid start file`);
|
||||
// throw new Error(`Function ${name} does not have a valid start file`);
|
||||
}
|
||||
originalScriptFile = scriptFile;
|
||||
}
|
||||
|
||||
// TODO: improve the logic for choosing entry point - failure sure not all scenarios are covered here.
|
||||
// TODO: Have to overwrite this entryPoint later on. Using temporary setting for now.
|
||||
// TODO: improve the logic for choosing entry point - failure sure not all scenarios are covered here.
|
||||
// TODO: Have to overwrite this entryPoint later on. Using temporary setting for now.
|
||||
if (fxJson._originalEntryPoint) {
|
||||
debug("Found originalEntryPoint setting: %s", fxJson._originalEntryPoint);
|
||||
entryPoint = fxJson._originalEntryPoint;
|
||||
|
@ -103,11 +104,11 @@ export class PackhostGenerator {
|
|||
|
||||
debug("Loaded function(%s) using entryPoint: %s - scriptFile: %s", name, scriptFile, entryPoint);
|
||||
return Promise.resolve({
|
||||
name,
|
||||
scriptFile,
|
||||
entryPoint,
|
||||
_originalEntryPoint: originalEntryPoint,
|
||||
_originalScriptFile: originalScriptFile,
|
||||
entryPoint,
|
||||
name,
|
||||
scriptFile,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -126,9 +127,13 @@ export class PackhostGenerator {
|
|||
debug("Generating host file");
|
||||
const exportStrings: string[] = [];
|
||||
|
||||
const outputDirPath = path.join(this.options.projectRootPath, this.options.outputPath);
|
||||
const relPath = path.relative(outputDirPath, this.options.projectRootPath);
|
||||
const rootRelPath = (path.sep === "\\") ? relPath.replace(/\\/g, "/") : relPath;
|
||||
|
||||
for (const [name, fx] of this.functionsMap) {
|
||||
const fxvar = this.safeFunctionName(fx.name);
|
||||
let exportStmt = ` "${fxvar}": require("../${fx.name}/${fx._originalScriptFile}")`;
|
||||
let exportStmt = ` "${fxvar}": require("${rootRelPath}/${fx.name}/${fx._originalScriptFile}")`;
|
||||
if (fx.entryPoint) {
|
||||
exportStmt += `.${fx.entryPoint}`;
|
||||
}
|
||||
|
@ -150,15 +155,26 @@ export class PackhostGenerator {
|
|||
debug("Updating Function JSONS");
|
||||
for (const [name, fx] of this.functionsMap) {
|
||||
debug("Updating function(%s)", name);
|
||||
const fxJsonPath = path.resolve(this.options.projectRootPath, name, "function.json");
|
||||
let fxJsonPath = path.resolve(this.options.projectRootPath, name, "function.json");
|
||||
const fxvar = this.safeFunctionName(fx.name);
|
||||
const fxJson = await FileHelper.readFileAsJSON(fxJsonPath);
|
||||
|
||||
if (this.options.copyToOutput) {
|
||||
await FileHelper.cp(
|
||||
path.resolve(this.options.projectRootPath, name, "function.json")
|
||||
, path.resolve(this.options.projectRootPath, this.options.outputPath, name, "function.json"));
|
||||
}
|
||||
|
||||
// TODO: This way of keeping track of the original settings is hacky
|
||||
fxJson._originalEntryPoint = fx._originalEntryPoint;
|
||||
fxJson._originalScriptFile = fx._originalScriptFile;
|
||||
fxJson.scriptFile = `../${this.options.outputPath}/${this.options.indexFileName}`;
|
||||
fxJson.scriptFile = this.options.copyToOutput ?
|
||||
`../${this.options.indexFileName}` :
|
||||
`../${this.options.outputPath}/${this.options.indexFileName}`;
|
||||
fxJson.entryPoint = fxvar;
|
||||
if (this.options.copyToOutput) {
|
||||
fxJsonPath = path.resolve(this.options.projectRootPath, this.options.outputPath, name, "function.json");
|
||||
}
|
||||
await FileHelper.overwriteFileUtf8(fxJsonPath, JSON.stringify(fxJson, null, " "));
|
||||
}
|
||||
}
|
||||
|
@ -172,6 +188,7 @@ export interface IPackhostGeneratorOptions {
|
|||
projectRootPath: string;
|
||||
outputPath?: string;
|
||||
indexFileName?: string;
|
||||
copyToOutput?: boolean;
|
||||
}
|
||||
|
||||
export interface IFxFunction {
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
import * as fs from "fs";
|
||||
import * as mkdirp from "mkdirp";
|
||||
import { ncp } from "ncp";
|
||||
import * as nodepath from "path";
|
||||
import * as rimraf from "rimraf";
|
||||
|
||||
export type FilterCallBack = (name: string) => boolean;
|
||||
|
||||
export class FileHelper {
|
||||
public static readdir(path: string): Promise<string[]> {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
@ -110,4 +115,38 @@ export class FileHelper {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
public static cp(source: string, destination: string, filter?: RegExp | FilterCallBack): Promise<{}> {
|
||||
const options: any = {};
|
||||
options.clobber = true;
|
||||
options.errs = process.stderr;
|
||||
if (filter) {
|
||||
options.filter = filter;
|
||||
}
|
||||
|
||||
return new Promise(async (resolve, reject) => {
|
||||
if (!await FileHelper.exists(nodepath.dirname(destination))) {
|
||||
await FileHelper.mkdirp(nodepath.dirname(destination));
|
||||
}
|
||||
|
||||
ncp(source, destination, options, (err) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public static mkdirp(pathToCreate: string): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
mkdirp(pathToCreate, (err, made) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(made);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
export * from "./fs-helper";
|
||||
export * from "./config-loader";
|
||||
export * from "./config-loader";
|
||||
|
|
|
@ -0,0 +1,258 @@
|
|||
import * as chai from "chai";
|
||||
import { spawn } from "child_process";
|
||||
import * as debug from "debug";
|
||||
import "mocha";
|
||||
import * as os from "os";
|
||||
import * as path from "path";
|
||||
import { FileHelper } from "../../src/utils/index";
|
||||
import { FunctionHostHarness } from "../util/FunctionHostHarness";
|
||||
import { ProcessHelper } from "../util/ProcessHelper";
|
||||
|
||||
const log = debug("azure-functions-pack:e2e.test");
|
||||
|
||||
const expect = chai.expect;
|
||||
const sampleRoot = path.resolve(__dirname, "./sample/");
|
||||
|
||||
describe("e2e tests", function() {
|
||||
|
||||
describe("funcpack pack .", function() {
|
||||
const randomNumber = Math.floor(Math.random() * 10000);
|
||||
const testRoot = path.resolve(os.tmpdir(), `./AzureFunctionsPackTest${randomNumber}`);
|
||||
log(`Using temp dir: ${testRoot}`);
|
||||
describe("cli", function() {
|
||||
before(async function() {
|
||||
this.timeout(60000);
|
||||
return await FileHelper.cp(sampleRoot, testRoot);
|
||||
});
|
||||
|
||||
after(async function() {
|
||||
this.timeout(60000);
|
||||
if (process.env.FUNCPACK_TESTS_CLEAN) {
|
||||
return await FileHelper.rimraf(testRoot);
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
});
|
||||
|
||||
it("should run successfully", async function() {
|
||||
this.timeout(60000);
|
||||
try {
|
||||
const results = await ProcessHelper.run(["node",
|
||||
path.resolve(__dirname, "../../lib/main.js"), "pack", "."], testRoot);
|
||||
expect(results.didError).to.be.equal(false, "funcpack pack did not exit successfully");
|
||||
return Promise.resolve();
|
||||
} catch (e) {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("host", function() {
|
||||
let host: FunctionHostHarness;
|
||||
|
||||
before(async function() {
|
||||
this.timeout(60000);
|
||||
await ProcessHelper.killAllFunctionsHosts();
|
||||
if (!await FileHelper.exists(testRoot)) {
|
||||
await FileHelper.cp(sampleRoot, testRoot);
|
||||
}
|
||||
await ProcessHelper.run(["node",
|
||||
path.resolve(__dirname, "../../lib/main.js"), "pack", "."], testRoot);
|
||||
host = new FunctionHostHarness(testRoot);
|
||||
await host.init();
|
||||
return new Promise((resolve, reject) => {
|
||||
const int = setInterval(() => {
|
||||
host.test("simple")
|
||||
.then((res: any) => {
|
||||
log(JSON.stringify(res));
|
||||
if (res.status === 200) {
|
||||
clearTimeout(int);
|
||||
resolve();
|
||||
}
|
||||
}).catch((e) => log(e));
|
||||
}, 500);
|
||||
});
|
||||
});
|
||||
|
||||
after(async function() {
|
||||
this.timeout(60000);
|
||||
host.stop();
|
||||
await ProcessHelper.killAllFunctionsHosts();
|
||||
if (process.env.FUNCPACK_TESTS_CLEAN) {
|
||||
return await FileHelper.rimraf(testRoot);
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
});
|
||||
|
||||
it("should ignore non-js files", function(done) {
|
||||
const funcname = process.env.FUNCPACK_TESTS_V2 ? "cs-ignoreme-v2" : "cs-ignoreme";
|
||||
host.test(funcname)
|
||||
.expect(200, done);
|
||||
});
|
||||
|
||||
it("should obey entryPoint setting", function(done) {
|
||||
host.test("entryPoint")
|
||||
.expect(200, done);
|
||||
});
|
||||
|
||||
it("should obey excluded setting", function(done) {
|
||||
host.test("excluded")
|
||||
.expect(200, done);
|
||||
});
|
||||
|
||||
it("should work with external script files", function(done) {
|
||||
host.test("externalScriptFile")
|
||||
.expect(200, done);
|
||||
});
|
||||
|
||||
it("should work with large imports", function(done) {
|
||||
host.test("largeimport")
|
||||
.expect(200, done);
|
||||
});
|
||||
|
||||
it("should work with local libs", function(done) {
|
||||
host.test("libimport")
|
||||
.expect(200, done);
|
||||
});
|
||||
|
||||
it("should obey scriptFile setting", function(done) {
|
||||
host.test("scriptFile")
|
||||
.expect(200, done);
|
||||
});
|
||||
|
||||
it("should work with simple functions", function(done) {
|
||||
host.test("simple")
|
||||
.expect(200, done);
|
||||
});
|
||||
|
||||
it("should work with simple imports", function(done) {
|
||||
host.test("simpleimport")
|
||||
.expect(200, done);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("funcpack pack -c .", function() {
|
||||
const randomNumber = Math.floor(Math.random() * 10000);
|
||||
const testRoot = path.resolve(os.tmpdir(), `./AzureFunctionsPackTest${randomNumber}`);
|
||||
log(`Using temp dir: ${testRoot}`);
|
||||
describe("cli", function() {
|
||||
|
||||
before(async function() {
|
||||
this.timeout(60000);
|
||||
return await FileHelper.cp(sampleRoot, testRoot);
|
||||
});
|
||||
|
||||
after(async function() {
|
||||
this.timeout(60000);
|
||||
if (process.env.FUNCPACK_TESTS_CLEAN) {
|
||||
return await FileHelper.rimraf(testRoot);
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
});
|
||||
|
||||
it("should run successfully", async function() {
|
||||
this.timeout(60000);
|
||||
try {
|
||||
const results = await ProcessHelper.run(["node",
|
||||
path.resolve(__dirname, "../../lib/main.js"), "pack", "-c", "."], testRoot);
|
||||
expect(results.didError).to.be.equal(false, "funcpack pack did not exit successfully");
|
||||
return Promise.resolve();
|
||||
} catch (e) {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("host", function() {
|
||||
let host: FunctionHostHarness;
|
||||
|
||||
before(async function() {
|
||||
this.timeout(60000);
|
||||
await ProcessHelper.killAllFunctionsHosts();
|
||||
if (!await FileHelper.exists(testRoot)) {
|
||||
await FileHelper.cp(sampleRoot, testRoot);
|
||||
}
|
||||
await ProcessHelper.run(["node",
|
||||
path.resolve(__dirname, "../../lib/main.js"), "pack", "-c", "."], testRoot);
|
||||
const testRunRoot = path.resolve(testRoot, ".funcpack");
|
||||
log(`Starting host in ${testRunRoot}`);
|
||||
host = new FunctionHostHarness(testRunRoot);
|
||||
await host.init();
|
||||
return new Promise((resolve, reject) => {
|
||||
const int = setInterval(() => {
|
||||
host.test("simple")
|
||||
.then((res: any) => {
|
||||
log(JSON.stringify(res, null, " "));
|
||||
if (res.status === 200) {
|
||||
clearTimeout(int);
|
||||
resolve();
|
||||
}
|
||||
}).catch((e) => log(e));
|
||||
}, 500);
|
||||
});
|
||||
});
|
||||
|
||||
after(async function() {
|
||||
this.timeout(60000);
|
||||
host.stop();
|
||||
await ProcessHelper.killAllFunctionsHosts();
|
||||
if (process.env.FUNCPACK_TESTS_CLEAN) {
|
||||
return await FileHelper.rimraf(testRoot);
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
});
|
||||
|
||||
it("should ignore non-js files", function(done) {
|
||||
const funcname = process.env.FUNCPACK_TESTS_V2 ? "cs-ignoreme-v2" : "cs-ignoreme";
|
||||
host.test(funcname)
|
||||
.expect(200, done);
|
||||
});
|
||||
|
||||
it("should obey entryPoint setting", function(done) {
|
||||
host.test("entryPoint")
|
||||
.expect(200, done);
|
||||
});
|
||||
|
||||
it("should obey excluded setting", function(done) {
|
||||
host.test("excluded")
|
||||
.expect(200, done);
|
||||
});
|
||||
|
||||
it("should work with external script files", function(done) {
|
||||
host.test("externalScriptFile")
|
||||
.expect(200, done);
|
||||
});
|
||||
|
||||
it("should work with large imports", function(done) {
|
||||
host.test("largeimport")
|
||||
.expect(200, done);
|
||||
});
|
||||
|
||||
it("should work with local libs", function(done) {
|
||||
host.test("libimport")
|
||||
.expect(200, done);
|
||||
});
|
||||
|
||||
it("should obey scriptFile setting", function(done) {
|
||||
host.test("scriptFile")
|
||||
.expect(200, done);
|
||||
});
|
||||
|
||||
it("should work with simple functions", function(done) {
|
||||
host.test("simple")
|
||||
.expect(200, done);
|
||||
});
|
||||
|
||||
it("should work with simple imports", function(done) {
|
||||
host.test("simpleimport")
|
||||
.expect(200, done);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
#r "Newtonsoft.Json"
|
||||
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Newtonsoft.Json;
|
||||
using System.Net;
|
||||
|
||||
public static IActionResult Run(HttpRequest req, TraceWriter log)
|
||||
{
|
||||
return (ActionResult)new OkObjectResult("never pack me");
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"disabled": false,
|
||||
"bindings": [
|
||||
{
|
||||
"authLevel": "function",
|
||||
"name": "req",
|
||||
"type": "httpTrigger",
|
||||
"direction": "in"
|
||||
},
|
||||
{
|
||||
"name": "$return",
|
||||
"type": "http",
|
||||
"direction": "out"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -3,4 +3,4 @@ using System.Net;
|
|||
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
|
||||
{
|
||||
return req.CreateResponse(HttpStatusCode.OK, "Never pack me");
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -14,6 +14,6 @@
|
|||
"tedious": "^1.14.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai":"3.5.0"
|
||||
"chai": "3.5.0"
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
import * as chai from "chai";
|
||||
import { PackhostGenerator } from "../src/packhost-generator";
|
||||
|
||||
const expect = chai.expect;
|
||||
|
||||
// TODO: Should write some tests :3
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"extends": "../tslint.json",
|
||||
"rules":{
|
||||
"no-console": false,
|
||||
"only-arrow-functions": false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
import { ChildProcess, spawn } from "child_process";
|
||||
import * as debug from "debug";
|
||||
import * as events from "events";
|
||||
|
||||
// tslint:disable-next-line:no-var-requires
|
||||
const ps = require("ps-node");
|
||||
|
||||
type ps = any;
|
||||
|
||||
const log = debug("azure-functions-pack:FunctionHost");
|
||||
|
||||
export class FunctionHost extends events.EventEmitter {
|
||||
private child: ChildProcess;
|
||||
private funcRoot: string;
|
||||
|
||||
constructor(funcRoot: string) {
|
||||
super();
|
||||
this.funcRoot = funcRoot;
|
||||
}
|
||||
|
||||
public start() {
|
||||
const commands = ["func host start"];
|
||||
log(`Running ${commands.join(" ")} in ${this.funcRoot}`);
|
||||
|
||||
const isWin = /^win/.test(process.platform);
|
||||
commands.unshift(isWin ? "/c" : "-c");
|
||||
|
||||
this.child = spawn(isWin ? "cmd" : "sh", commands, {
|
||||
cwd: this.funcRoot,
|
||||
});
|
||||
if (process.env.DEBUG
|
||||
&& (process.env.DEBUG.includes("azure-functions-pack:*")
|
||||
|| process.env.DEBUG.includes("azure-functions-pack:FunctionHost"))) {
|
||||
this.child.stdout.pipe(process.stdout);
|
||||
this.child.stderr.pipe(process.stderr);
|
||||
}
|
||||
this.child.on("error", (err: Error) => {
|
||||
this.emit("error", err);
|
||||
});
|
||||
this.child.on("exit", (code: string) => {
|
||||
this.emit("exit", code);
|
||||
});
|
||||
}
|
||||
|
||||
public stop(): Promise<{}> {
|
||||
return new Promise((resolve, reject) => {
|
||||
ps.kill(this.child.pid, (err: Error) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
import * as debug from "debug";
|
||||
import * as request from "supertest";
|
||||
import { FunctionHost } from "./FunctionHost";
|
||||
|
||||
const log = debug("azure-functions-pack:FunctionHostHarness");
|
||||
|
||||
export class FunctionHostHarness {
|
||||
private host: FunctionHost;
|
||||
private request: request.SuperTest<request.Test>;
|
||||
|
||||
constructor(funcRoot: string) {
|
||||
this.host = new FunctionHost(funcRoot);
|
||||
this.request = request("http://localhost:7071");
|
||||
|
||||
this.host.on("error", (err: Error) => {
|
||||
log(err);
|
||||
});
|
||||
|
||||
this.host.on("exit", (code: string) => {
|
||||
log(`Functions host exitted with status code: ${code}`);
|
||||
});
|
||||
}
|
||||
|
||||
public test(name: string) {
|
||||
return this.request.post(`/api/${name}`);
|
||||
}
|
||||
|
||||
public init(): Promise<{}> {
|
||||
const req = this.request;
|
||||
this.host.start();
|
||||
return new Promise((resolve, reject) => {
|
||||
const int = setInterval(() => {
|
||||
req.get("/admin/host/status")
|
||||
.then((res) => {
|
||||
if (res.status === 200) {
|
||||
clearTimeout(int);
|
||||
resolve();
|
||||
}
|
||||
}).catch((e) => log(e));
|
||||
}, 500);
|
||||
});
|
||||
}
|
||||
|
||||
public stop() {
|
||||
this.host.stop();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
import { ChildProcess, exec, spawn } from "child_process";
|
||||
import * as debug from "debug";
|
||||
|
||||
// tslint:disable-next-line:no-var-requires
|
||||
const ps = require("ps-node");
|
||||
|
||||
const log = debug("azure-functions-pack:ProcessHelper");
|
||||
|
||||
export class ProcessHelper {
|
||||
public static run(commands: string[], cwd: string): Promise<IProcessResults> {
|
||||
return new Promise<IProcessResults>((resolve, reject) => {
|
||||
exec(commands.join(" "), { cwd }, (err: Error, stdout: string, stderr: string) => {
|
||||
log(stdout);
|
||||
log(stderr);
|
||||
const results: IProcessResults = {
|
||||
didError: err ? true : false,
|
||||
error: err,
|
||||
stderr,
|
||||
stdout,
|
||||
};
|
||||
if (err) {
|
||||
log(err);
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
resolve(results);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public static killAllFunctionsHosts(): Promise<{}> {
|
||||
return new Promise((resolve, reject) => {
|
||||
ps.lookup({
|
||||
command: "dotnet",
|
||||
}, (err: any, processes: any) => {
|
||||
const promises: any[] = [];
|
||||
processes.forEach((p: any) => {
|
||||
log(JSON.stringify(p, null, " "));
|
||||
p.arguments.forEach((a: any) => {
|
||||
if (a.includes("bin/Azure.Functions.Cli.dll")) {
|
||||
promises.push(ProcessHelper.kill(p.pid));
|
||||
}
|
||||
});
|
||||
});
|
||||
Promise.all(promises).then(resolve).catch((e) => { log(e); resolve(); });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public static kill(pid: number): Promise<{}> {
|
||||
return new Promise((resolve, reject) => {
|
||||
ps.kill(pid, "SIGKILL", (e: Error) => {
|
||||
if (e) {
|
||||
resolve(e);
|
||||
} else {
|
||||
reject();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export interface IProcessResults {
|
||||
exitCode?: string;
|
||||
stdout?: string;
|
||||
stderr?: string;
|
||||
didError?: boolean;
|
||||
error?: Error;
|
||||
}
|
Загрузка…
Ссылка в новой задаче