From dbfc14a48f5b8ef2880eb29d1b6b5ce966940340 Mon Sep 17 00:00:00 2001 From: Orta Therox Date: Tue, 19 Sep 2017 09:59:16 -0400 Subject: [PATCH 1/8] Handle relative path resolving --- dangerfile.ts | 54 ++++++------- package.json | 2 +- source/ambient.d.ts | 129 ------------------------------ source/danger.d.ts | 2 +- source/runner/DangerfileRunner.ts | 12 +-- tsconfig.json | 14 +--- yarn.lock | 15 ++-- 7 files changed, 40 insertions(+), 188 deletions(-) diff --git a/dangerfile.ts b/dangerfile.ts index 3c3d25484..5996db7e9 100644 --- a/dangerfile.ts +++ b/dangerfile.ts @@ -4,27 +4,21 @@ import { DangerDSL } from "./source/dsl/DangerDSL" declare var danger: DangerDSL -declare var results: any +// declare var results: any declare function warn(params: string): void declare function fail(params: string): void -declare function message(params: string): void -declare function markdown(params: string): void +// declare function message(params: string): void +// declare function markdown(params: string): void +declare function schedule(promise: Promise): void declare function schedule(promise: () => Promise): void -declare function schedule(callback: (resolve) => void): void +declare function schedule(callback: (resolve: any) => void): void import * as fs from "fs" -import * as child_process from "child_process" -import { distanceInWords } from "date-fns" - -// For some reason we're getting type errors on this includes module? -// Wonder if we could move to the includes function in ES2015? -import * as includes from "lodash.includes" -const sentence = danger.utils.sentence schedule(async () => { // Request a CHANGELOG entry if not declared #trivial - const hasChangelog = includes(danger.git.modified_files, "changelog.md") - const isTrivial = includes(danger.github.pr.body + danger.github.pr.title, "#trivial") + const hasChangelog = danger.git.modified_files.includes("changelog.md") + const isTrivial = (danger.github.pr.body + danger.github.pr.title).includes("#trivial") const isGreenkeeper = danger.github.pr.user.login === "greenkeeper" if (!hasChangelog && !isTrivial && !isGreenkeeper) { @@ -33,7 +27,7 @@ schedule(async () => { // Politely ask for their name on the entry too const changelogDiff = await danger.git.diffForFile("changelog.md") const contributorName = danger.github.pr.user.login - if (changelogDiff && !includes(changelogDiff.diff, contributorName)) { + if (changelogDiff && changelogDiff.diff.includes(contributorName)) { warn("Please add your GitHub name to the changelog entry, so we can attribute you correctly.") } } @@ -48,23 +42,23 @@ schedule(yarn()) // This also serves as the "one true DSL" for a Danger run against a PR // which tools can then work against. -// import dtsGenerator from "./scripts/danger-dts" -// const currentDTS = dtsGenerator() -// const savedDTS = fs.readFileSync("source/danger.d.ts").toString() -// if (currentDTS !== savedDTS) { -// const message = "There are changes to the Danger DSL which are not reflected in the current danger.d.ts." -// const idea = "Please run yarn declarations and update this PR." -// fail(`${message}
${idea}`) -// } +import dtsGenerator from "./scripts/danger-dts" +const currentDTS = dtsGenerator() +const savedDTS = fs.readFileSync("source/danger.d.ts").toString() +if (currentDTS !== savedDTS) { + const message = "There are changes to the Danger DSL which are not reflected in the current danger.d.ts." + const idea = "Please run yarn declarations and update this PR." + fail(`${message}
${idea}`) +} // Always ensure we name all CI providers in the README. These // regularly get forgotten on a PR adding a new one. +const sentence = danger.utils.sentence -// import { realProviders } from "./source/ci_source/providers" -// import Fake from "./source/ci_source/providers/Fake" -// const readme = fs.readFileSync("README.md").toString() -// const names = realProviders.map(p => new p({}).name) -// const missing = names.filter(n => !readme.includes(n)) -// if (missing.length) { -// warn(`These providers are missing from the README: ${sentence(missing)}`) -// } +import { realProviders } from "./source/ci_source/providers" +const readme = fs.readFileSync("README.md").toString() +const names = realProviders.map(p => new p({}).name) +const missing = names.filter(n => !readme.includes(n)) +if (missing.length) { + warn(`These providers are missing from the README: ${sentence(missing)}`) +} diff --git a/package.json b/package.json index 79c0fa535..9f06fec05 100644 --- a/package.json +++ b/package.json @@ -104,7 +104,7 @@ "parse-link-header": "^1.0.1", "pinpoint": "^1.1.0", "rfc6902": "^1.3.0", - "vm2": "patriksimek/vm2", + "vm2": "patriksimek/vm2#custom_files", "voca": "^1.2.0" }, "optionalDependencies": {} diff --git a/source/ambient.d.ts b/source/ambient.d.ts index 1b29e1145..1fa4d7f58 100644 --- a/source/ambient.d.ts +++ b/source/ambient.d.ts @@ -14,132 +14,3 @@ declare module "parse-link-header" declare module "pinpoint" declare module "*/package.json" - -declare module "vm2" { - /** - * Require options for a VM - */ - export interface VMRequire { - /** Array of allowed builtin modules, accepts ["*"] for all (default: none) */ - builtin?: string[] - /* - * `host` (default) to require modules in host and proxy them to sandbox. `sandbox` to load, compile and - * require modules in sandbox. Builtin modules except `events` always required in host and proxied to sandbox - */ - context?: "host" | "sandbox" - /** `true` or an array of allowed external modules (default: `false`) */ - external?: boolean | string[] - /** Array of modules to be loaded into NodeVM on start. */ - import?: string[] - /** Restricted path where local modules can be required (default: every path). */ - root?: string - /** Collection of mock modules (both external or builtin). */ - mock?: any - } - - /** - * A custom compiler function for all of the JS that comes - * into the VM - */ - type CompilerFunction = (code: string, filename: string) => string - - /** - * Options for creating a NodeVM - */ - export interface VMOptions { - /** - * `javascript` (default) or `coffeescript` or custom compiler function (which receives the code, and it's filepath). - * The library expects you to have coffee-script pre-installed if the compiler is set to `coffeescript`. - */ - compiler?: "javascript" | "coffeescript" | CompilerFunction - /** VM's global object. */ - sandbox?: any - /** - * Script timeout in milliseconds. Timeout is only effective on code you run through `run`. - * Timeout is NOT effective on any method returned by VM. - */ - timeout?: number - } - - /** - * Options specific o - */ - export interface NodeVMOptions extends VMOptions { - /** `inherit` to enable console, `redirect` to redirect to events, `off` to disable console (default: `inherit`). */ - console?: "inherit" | "redirect" - /** `true` or an object to enable `require` optionss (default: `false`). */ - require?: true | VMRequire - /** `true` to enable VMs nesting (default: `false`). */ - nesting?: boolean - /** `commonjs` (default) to wrap script into CommonJS wrapper, `none` to retrieve value returned by the script. */ - wrapper?: "commonjs" | "none" - } - - /** - * A VM with behavior more similar to running inside Node. - */ - export class NodeVM { - constructor(options?: NodeVMOptions) - /** Runs the code */ - run(js: string, path: string): any - /** Runs the VMScript object */ - run(script: VMScript): any - - /** Freezes the object inside VM making it read-only. Not available for primitive values. */ - freeze(object: any, name: string): any - /** Protects the object inside VM making impossible to set functions as it's properties. Not available for primitive values. */ - protect(object: any, name: string): any - /** Require a module in VM and return it's exports. */ - require(module: string): any - } - - /** - * VM is a simple sandbox, without `require` feature, to synchronously run an untrusted code. - * Only JavaScript built-in objects + Buffer are available. Scheduling functions - * (`setInterval`, `setTimeout` and `setImmediate`) are not available by default. - */ - export class VM { - constructor(options?: VMOptions) - /** Runs the code */ - run(js: string): any - /** Runs the VMScript object */ - run(script: VMScript): any - /** Freezes the object inside VM making it read-only. Not available for primitive values. */ - freeze(object: any, name: string): any - /** Protects the object inside VM making impossible to set functions as it's properties. Not available for primitive values */ - protect(object: any, name: string): any - - /** - * Create NodeVM and run code inside it. - * - * @param {String} script Javascript code. - * @param {String} [filename] File name (used in stack traces only). - * @param {Object} [options] VM options. - */ - static code(script: string, filename: string, options: NodeVMOptions): NodeVM - - /** - * Create NodeVM and run script from file inside it. - * - * @param {String} [filename] File name (used in stack traces only). - * @param {Object} [options] VM options. - */ - static file(filename: string, options: NodeVMOptions): NodeVM - } - - /** - * You can increase performance by using pre-compiled scripts. - * The pre-compiled VMScript can be run later multiple times. It is important to note that the code is not bound - * to any VM (context); rather, it is bound before each run, just for that run. - */ - export class VMScript { - constructor(code: string, path: string) - /** Wraps the code */ - wrap(prefix: string, postfix: string): VMScript - /** Compiles the code. If called multiple times, the code is only compiled once. */ - compile(): any - } - - /** Custom Error class */ - export class VMError extends Error {} -} diff --git a/source/danger.d.ts b/source/danger.d.ts index 367f24f81..fb84706ae 100644 --- a/source/danger.d.ts +++ b/source/danger.d.ts @@ -167,7 +167,7 @@ declare module "danger" { after: any /** If both before & after are arrays, then you optionally get what is added. Empty if no additional objects. */ added?: any[] - /** If both before & after are arrays, then you optionally get what is removed. Empty ig no removed objects. */ + /** If both before & after are arrays, then you optionally get what is removed. Empty if no removed objects. */ removed?: any[] } diff --git a/source/runner/DangerfileRunner.ts b/source/runner/DangerfileRunner.ts index 9e106d9bb..5b563a8e7 100644 --- a/source/runner/DangerfileRunner.ts +++ b/source/runner/DangerfileRunner.ts @@ -92,23 +92,13 @@ export async function runDangerfileEnvironment( originalContents: string | undefined, environment: NodeVMOptions ): Promise { + environment.sourceExtensions = ["js", "ts"] const vm = new NodeVM(environment) // Require our dangerfile originalContents = originalContents || fs.readFileSync(filename, "utf8") let content = cleanDangerfile(originalContents) - // TODO: Relative imports get TS/Babel - - // var Module = require("module") - // var originalRequire = Module.prototype.require - - // Module.prototype.require = function() { - // //do your thing here - // console.log(arguments) - // return originalRequire.apply(this, arguments) - // } - try { vm.run(content, filename) diff --git a/tsconfig.json b/tsconfig.json index e06780b94..af915551a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,21 +14,13 @@ "module": "commonjs", "moduleResolution": "node", "pretty": true, - "target": "es5", + "target": "es2015", "outDir": "distribution", - "lib": ["es5", "es2015"] + "lib": ["es5", "es2017"] }, "formatCodeOptions": { "indentSize": 2, "tabSize": 2 }, - "exclude": [ - "dangerfile.ts", - "scripts", - "node_modules", - "source/**/fixtures/*", - "source/**/_tests/*", - "distribution", - "types" - ] + "exclude": ["scripts", "node_modules", "source/**/fixtures/*", "source/**/_tests/*", "distribution", "types"] } diff --git a/yarn.lock b/yarn.lock index 19920ba5a..0efe7e913 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1271,13 +1271,14 @@ cssom@0.3.x, "cssom@>= 0.3.0 < 0.4.0", "cssom@>= 0.3.2 < 0.4.0": cssom "0.3.x" danger-plugin-yarn@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/danger-plugin-yarn/-/danger-plugin-yarn-1.1.1.tgz#6ecd03e221ae8b71f51e089150a766e8c8d4b2c3" + version "1.2.0" + resolved "https://registry.yarnpkg.com/danger-plugin-yarn/-/danger-plugin-yarn-1.2.0.tgz#3681db67f71e65dc6dd7a0a31ef3e7f56e9b29d3" dependencies: date-fns "^1.28.5" lodash.flatten "^4.4.0" lodash.includes "^4.3.0" node-fetch "^1.7.1" + semver "^5.4.1" optionalDependencies: esdoc "^0.5.2" @@ -3935,6 +3936,10 @@ sax@^1.1.4, sax@^1.2.1: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" +semver@^5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" + semver@~5.0.1: version "5.0.3" resolved "https://registry.yarnpkg.com/semver/-/semver-5.0.3.tgz#77466de589cd5d3c95f138aa78bc569a3cb5d27a" @@ -4462,9 +4467,9 @@ verror@1.3.6: dependencies: extsprintf "1.0.2" -vm2@patriksimek/vm2: - version "3.4.6" - resolved "https://codeload.github.com/patriksimek/vm2/tar.gz/0711926ba6e6fbfb89277d8033cfd11b69888e46" +vm2@patriksimek/vm2#custom_files: + version "3.5.0" + resolved "https://codeload.github.com/patriksimek/vm2/tar.gz/7e82f90ac705fc44fad044147cb0df09b4c79a57" voca@^1.2.0: version "1.3.0" From 971b1f0ff0ea2cdd6ff7fff8232dc2027edc6089 Mon Sep 17 00:00:00 2001 From: Orta Therox Date: Tue, 19 Sep 2017 11:39:33 -0400 Subject: [PATCH 2/8] Adds a production build tsconfig --- package.json | 8 ++++---- source/runner/DangerfileRunner.ts | 3 ++- tsconfig.json | 4 ++-- tsconfig.production.json | 12 ++++++++++++ 4 files changed, 20 insertions(+), 7 deletions(-) create mode 100644 tsconfig.production.json diff --git a/package.json b/package.json index 9f06fec05..c274fa959 100644 --- a/package.json +++ b/package.json @@ -30,15 +30,15 @@ }, "scripts": { "precommit": "lint-staged", - "prepush": "npm run build", + "prepush": "yarn run build", "test": "jest", "test:watch": "jest --watch", "lint": "tslint \"source/**/*.ts\"", "lint:fix": "tslint \"source/**/*.ts\" --fix", - "prepublishOnly": "npm run build && yarn declarations", - "build": "shx rm -rf ./distribution && tsc && madge ./distribution --circular", + "prepublishOnly": "yarn run build && yarn declarations", + "build": "shx rm -rf ./distribution && tsc -p tsconfig.production.json && madge ./distribution --circular", "build:watch": "tsc -w", - "link": "npm run build && chmod +x distribution/commands/danger.js && npm link", + "link": "yarn run build && chmod +x distribution/commands/danger.js && npm link", "declarations": "ts-node ./scripts/create-danger-dts.ts", "docs:cp_defs": "mkdir docs/docs_generate; cp source/danger.d.ts docs/docs_generate; cp node_modules/github/lib/index.d.ts docs/docs_generate/github.d.ts", diff --git a/source/runner/DangerfileRunner.ts b/source/runner/DangerfileRunner.ts index 5b563a8e7..447159af0 100644 --- a/source/runner/DangerfileRunner.ts +++ b/source/runner/DangerfileRunner.ts @@ -167,7 +167,8 @@ export function cleanDangerfile(contents: string): string { const typescriptify = (content: string): string => { const ts = require("typescript") // tslint:disable-line - let result = ts.transpileModule(content, {}) + const compilerOptions = JSON.parse(fs.readFileSync("tsconfig.json", "utf8")) + let result = ts.transpileModule(content, compilerOptions) return result.outputText } diff --git a/tsconfig.json b/tsconfig.json index af915551a..b9c93977b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,9 +14,9 @@ "module": "commonjs", "moduleResolution": "node", "pretty": true, - "target": "es2015", + "target": "es5", "outDir": "distribution", - "lib": ["es5", "es2017"] + "lib": ["es2017"] }, "formatCodeOptions": { "indentSize": 2, diff --git a/tsconfig.production.json b/tsconfig.production.json new file mode 100644 index 000000000..c66c485dc --- /dev/null +++ b/tsconfig.production.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.json", + "exclude": [ + "dangerfile.ts", + "scripts", + "node_modules", + "source/**/fixtures/*", + "source/**/_tests/*", + "distribution", + "types" + ] +} From 1e5cf37e02faf210e2b3fe4384168f18017ce1f4 Mon Sep 17 00:00:00 2001 From: Orta Therox Date: Sat, 23 Sep 2017 21:13:40 -0400 Subject: [PATCH 3/8] Split runners into vm based runner and local evaluation --- package.json | 1 + source/ambient.d.ts | 21 ++ source/commands/danger-pr.ts | 4 +- source/runner/DangerfileRunner.ts | 194 ------------------ source/runner/Executor.ts | 9 +- source/runner/_tests/_danger_runner.test.ts | 40 +--- .../fixtures/__DangerfileMultiScheduled.js | 54 +++-- .../runners/_tests/_cleanDangerfile.test.ts | 37 ++++ .../runner/runners/_tests/_transpiler.test.ts | 3 + source/runner/runners/inline.ts | 131 ++++++++++++ source/runner/runners/runner.ts | 29 +++ .../runner/runners/utils/cleanDangerfile.ts | 12 ++ .../runners/utils/resultsForCaughtError.ts | 28 +++ source/runner/runners/utils/transpiler.ts | 79 +++++++ source/runner/runners/vm2.ts | 83 ++++++++ yarn.lock | 4 + 16 files changed, 468 insertions(+), 261 deletions(-) delete mode 100644 source/runner/DangerfileRunner.ts create mode 100644 source/runner/runners/_tests/_cleanDangerfile.test.ts create mode 100644 source/runner/runners/_tests/_transpiler.test.ts create mode 100644 source/runner/runners/inline.ts create mode 100644 source/runner/runners/runner.ts create mode 100644 source/runner/runners/utils/cleanDangerfile.ts create mode 100644 source/runner/runners/utils/resultsForCaughtError.ts create mode 100644 source/runner/runners/utils/transpiler.ts create mode 100644 source/runner/runners/vm2.ts diff --git a/package.json b/package.json index c274fa959..88519a741 100644 --- a/package.json +++ b/package.json @@ -103,6 +103,7 @@ "parse-diff": "^0.4.0", "parse-link-header": "^1.0.1", "pinpoint": "^1.1.0", + "require-from-string": "^2.0.1", "rfc6902": "^1.3.0", "vm2": "patriksimek/vm2#custom_files", "voca": "^1.2.0" diff --git a/source/ambient.d.ts b/source/ambient.d.ts index 1fa4d7f58..4db69d35a 100644 --- a/source/ambient.d.ts +++ b/source/ambient.d.ts @@ -14,3 +14,24 @@ declare module "parse-link-header" declare module "pinpoint" declare module "*/package.json" + +declare module "require-from-string" + +// declare module "require-from-string" { +// export interface RequireOptions { +// /** List of paths, that will be appended to module paths. Useful, when you want +// * to be able require modules from these paths. */ +// appendPaths: string[] +// /** +// * Same as appendPath, but paths will be prepended. +// */ +// prependPaths: string[] +// } +// /** +// * Load module from string in Node. +// * @param code Module code +// * @param filename Optional filename +// * @param opts +// */ +// export default function(code: string, filename?: string, opts?: Partial): any +// } diff --git a/source/commands/danger-pr.ts b/source/commands/danger-pr.ts index 27f4b35ca..3d463ac92 100644 --- a/source/commands/danger-pr.ts +++ b/source/commands/danger-pr.ts @@ -7,7 +7,7 @@ import { GitHub } from "../platforms/GitHub" import { GitHubAPI } from "../platforms/github/GitHubAPI" import { Executor } from "../runner/Executor" import { pullRequestParser } from "../platforms/github/pullRequestParser" -import { runDangerfileEnvironment } from "../runner/DangerfileRunner" +import { runDangerfileEnvironment } from "../runner/runners/inline" import { dangerfilePath } from "./utils/file-utils" import validateDangerfileExists from "./utils/validateDangerfileExists" import openRepl from "./utils/repl" @@ -56,7 +56,7 @@ async function runDanger(source: FakeCI, platform: GitHub, file: string) { const runtimeEnv = await exec.setupDanger() const results = await runDangerfileEnvironment(file, undefined, runtimeEnv) if (program.repl) { - openRepl(runtimeEnv.sandbox) + openRepl(runtimeEnv) } else { jsome(results) } diff --git a/source/runner/DangerfileRunner.ts b/source/runner/DangerfileRunner.ts deleted file mode 100644 index 447159af0..000000000 --- a/source/runner/DangerfileRunner.ts +++ /dev/null @@ -1,194 +0,0 @@ -import * as fs from "fs" -import * as path from "path" -import * as pinpoint from "pinpoint" - -import { DangerResults } from "../dsl/DangerResults" -import { DangerContext } from "../runner/Dangerfile" - -import { NodeVM, NodeVMOptions } from "vm2" - -let hasNativeTypeScript = false -let hasBabel = false -let hasBabelTypeScript = false -let hasFlow = false - -declare const regeneratorRuntime: any - -// You know you're being a dangerous badass when you have this many linter disables. Deal with it. - -try { - require.resolve("typescript") // tslint:disable-line - hasNativeTypeScript = true -} catch (e) {} // tslint:disable-line - -try { - require.resolve("babel-core") // tslint:disable-line - require("babel-polyfill") // tslint:disable-line - hasBabel = true - - try { - require.resolve("babel-plugin-transform-typescript") // tslint:disable-line - hasBabelTypeScript = true - } catch (e) {} // tslint:disable-line - - try { - require.resolve("babel-plugin-transform-flow-strip-types") // tslint:disable-line - hasFlow = true - } catch (e) {} // tslint:disable-line -} catch (e) {} // tslint:disable-line - -/** - * Executes a Dangerfile at a specific path, with a context. - * The values inside a Danger context are applied as globals to the Dangerfiles runtime. - * - * @param {DangerContext} dangerfileContext the global danger context - */ -export async function createDangerfileRuntimeEnvironment(dangerfileContext: DangerContext): Promise { - // This is for plugin support, we now have the Danger objects inside Danger's - // main process context too. This means plugins that Danger depends on can also - // get support for the globals. - Object.keys(dangerfileContext).forEach(key => { - global[key] = dangerfileContext[key] - }) - - return { - require: { - external: true, - context: "host", - builtin: ["*"], - // import: hasBabel ? ["babel-polyfill"] : [], - }, - sandbox: { ...dangerfileContext, regeneratorRuntime: regeneratorRuntime }, - compiler: compile, - } -} - -function compile(code: string, filename: string) { - const filetype = path.extname(filename) - let result = code - if (hasNativeTypeScript && !filename.includes("node_modules") && filetype.startsWith(".ts")) { - result = typescriptify(code) - } else if (hasBabel && hasBabelTypeScript && !filename.includes("node_modules") && filetype.startsWith(".ts")) { - result = babelify(code, filename, ["transform-typescript"]) - } else if (hasBabel && !filename.includes("node_modules") && filetype.startsWith(".js")) { - result = babelify(code, filename, hasFlow ? ["transform-flow-strip-types"] : []) - } - - return result -} - -export type TranspileType = null | "babel" | "typescript" - -/** - * Executes a Dangerfile at a specific path, with a context. - * The values inside a Danger context are applied as globals to the Dangerfiles runtime. - * - * @param {string} filename the file path for the dangerfile - * @param {any} environment the results of createDangerfileRuntimeEnvironment - * @returns {DangerResults} the results of the run - */ -export async function runDangerfileEnvironment( - filename: string, - originalContents: string | undefined, - environment: NodeVMOptions -): Promise { - environment.sourceExtensions = ["js", "ts"] - const vm = new NodeVM(environment) - - // Require our dangerfile - originalContents = originalContents || fs.readFileSync(filename, "utf8") - let content = cleanDangerfile(originalContents) - - try { - vm.run(content, filename) - - const results = environment.sandbox!.results! - await Promise.all( - results.scheduled.map((fnOrPromise: any) => { - if (fnOrPromise instanceof Promise) { - return fnOrPromise - } - if (fnOrPromise.length === 1) { - // callback-based function - return new Promise(res => fnOrPromise(res)) - } - return fnOrPromise() - }) - ) - return { - fails: results.fails, - warnings: results.warnings, - messages: results.messages, - markdowns: results.markdowns, - } - } catch (error) { - console.error("Unable to evaluate the Dangerfile") - return resultsForCaughtError(filename, content, error) - } -} - -/** Returns Markdown results to post if an exception is raised during the danger run */ -const resultsForCaughtError = (file: string, contents: string, error: Error): DangerResults => { - const match = /(\d+:\d+)/g.exec(error.stack!) - let code - if (match) { - const [line, column] = match[0].split(":").map(value => parseInt(value, 10) - 1) - code = pinpoint(contents, { line, column }) - } else { - code = contents - } - const failure = `Danger failed to run \`${file}\`.` - const errorMD = `## Error ${error.name} -\`\`\` -${error.message} -${error.stack} -\`\`\` -### Dangerfile -\`\`\` -${code} -\`\`\` - ` - return { fails: [{ message: failure }], warnings: [], markdowns: [errorMD], messages: [] } -} - -// https://regex101.com/r/dUq4yB/1 -const requirePattern = /^.* require\(('|")danger('|")\);?$/gm -// https://regex101.com/r/dUq4yB/2 -const es6Pattern = /^.* from ('|")danger('|");?$/gm - -/** - * Updates a Dangerfile to remove the import for Danger - * @param {string} contents the file path for the dangerfile - * @returns {string} the revised Dangerfile - */ -export function cleanDangerfile(contents: string): string { - return contents.replace(es6Pattern, "// Removed import").replace(requirePattern, "// Removed require") -} - -const typescriptify = (content: string): string => { - const ts = require("typescript") // tslint:disable-line - const compilerOptions = JSON.parse(fs.readFileSync("tsconfig.json", "utf8")) - let result = ts.transpileModule(content, compilerOptions) - return result.outputText -} - -const babelify = (content: string, filename: string, extraPlugins: string[]): string => { - const babel = require("babel-core") // tslint:disable-line - if (!babel.transform) { - return content - } - - const options = babel.loadOptions ? babel.loadOptions({}) : { plugins: [] } - - const fileOpts = { - filename, - filenameRelative: filename, - sourceMap: false, - sourceFileName: null, - sourceMapTarget: null, - plugins: [...extraPlugins, ...options.plugins], - } - - const result = babel.transform(content, fileOpts) - return result.code -} diff --git a/source/runner/Executor.ts b/source/runner/Executor.ts index e580da769..fd926303e 100644 --- a/source/runner/Executor.ts +++ b/source/runner/Executor.ts @@ -1,16 +1,15 @@ -import { contextForDanger } from "../runner/Dangerfile" +import { contextForDanger, DangerContext } from "../runner/Dangerfile" import { DangerDSL } from "../dsl/DangerDSL" import { CISource } from "../ci_source/ci_source" import { Platform } from "../platforms/platform" import { DangerResults } from "../dsl/DangerResults" import { template as githubResultsTemplate } from "./templates/githubIssueTemplate" -import { createDangerfileRuntimeEnvironment, runDangerfileEnvironment } from "./DangerfileRunner" +import { createDangerfileRuntimeEnvironment, runDangerfileEnvironment } from "./runners/vm2" import exceptionRaisedTemplate from "./templates/exceptionRaisedTemplate" import * as debug from "debug" import * as chalk from "chalk" import { sentence, href } from "./DangerUtils" -import { NodeVMOptions } from "vm2" // This is still badly named, maybe it really should just be runner? @@ -44,7 +43,7 @@ export class Executor { * Runs all of the operations for a running just Danger * @returns {DangerfileRuntimeEnv} A runtime environment to run Danger in */ - async setupDanger(): Promise { + async setupDanger(): Promise { const dsl = await this.dslForDanger() const context = contextForDanger(dsl) return await createDangerfileRuntimeEnvironment(context) @@ -56,7 +55,7 @@ export class Executor { * @returns {Promise} The results of the Danger run */ - async runDanger(file: string, runtime: NodeVMOptions) { + async runDanger(file: string, runtime: DangerContext) { let results = {} as DangerResults // If an eval of the Dangerfile fails, we should generate a diff --git a/source/runner/_tests/_danger_runner.test.ts b/source/runner/_tests/_danger_runner.test.ts index c999a9428..0fb2ae050 100644 --- a/source/runner/_tests/_danger_runner.test.ts +++ b/source/runner/_tests/_danger_runner.test.ts @@ -1,5 +1,5 @@ import { contextForDanger } from "../Dangerfile" -import { createDangerfileRuntimeEnvironment, runDangerfileEnvironment, cleanDangerfile } from "../DangerfileRunner" +import { createDangerfileRuntimeEnvironment, runDangerfileEnvironment } from "../runners/inline" import { FakeCI } from "../../ci_source/providers/Fake" import { FakePlatform } from "../../platforms/FakePlatform" @@ -85,7 +85,7 @@ describe("with fixtures", () => { }) }) - it("handles multiple scheduled statements and all message types", async () => { + it.only("handles multiple scheduled statements and all message types", async () => { const context = await setupDangerfileContext() const runtime = await createDangerfileRuntimeEnvironment(context) const results = await runDangerfileEnvironment( @@ -184,39 +184,3 @@ describe("with fixtures", () => { expect(results.markdowns[0]).toContain("Error: failure") }) }) - -describe("cleaning Dangerfiles", () => { - it("also handles typescript style imports", () => { - const before = ` -import { danger, warn, fail, message } from 'danger' -import { danger, warn, fail, message } from "danger" -import { danger, warn, fail, message } from "danger"; -import danger from "danger" -import danger from 'danger' -import danger from 'danger'; -` - const after = ` -// Removed import -// Removed import -// Removed import -// Removed import -// Removed import -// Removed import -` - expect(cleanDangerfile(before)).toEqual(after) - }) - - it("also handles require style imports", () => { - const before = ` -const { danger, warn, fail, message } = require('danger') -var { danger, warn, fail, message } = require("danger") -let { danger, warn, fail, message } = require('danger'); -` - const after = ` -// Removed require -// Removed require -// Removed require -` - expect(cleanDangerfile(before)).toEqual(after) - }) -}) diff --git a/source/runner/_tests/fixtures/__DangerfileMultiScheduled.js b/source/runner/_tests/fixtures/__DangerfileMultiScheduled.js index 09bf6d822..7a076abe7 100644 --- a/source/runner/_tests/fixtures/__DangerfileMultiScheduled.js +++ b/source/runner/_tests/fixtures/__DangerfileMultiScheduled.js @@ -1,27 +1,37 @@ /*eslint-disable */ -schedule(new Promise((res) => { - setTimeout(() => { - fail('Asynchronous Failure'); - res(); - }, 100); -})); +debugger -schedule(new Promise((res) => { - warn('Asynchronous Warning'); - res(); -})); +schedule( + new Promise(res => { + setTimeout(() => { + fail("Asynchronous Failure") + res() + }, 100) + }) +) -schedule(new Promise((res) => { - setTimeout(() => { - message('Asynchronous Message'); - res(); - }, 10); -})); +schedule( + new Promise(res => { + warn("Asynchronous Warning") + res() + }) +) -schedule(new Promise((res) => { - setTimeout(() => { - markdown('Asynchronous Markdown'); - res(); - }, 50); -})); \ No newline at end of file +schedule( + new Promise(res => { + setTimeout(() => { + message("Asynchronous Message") + res() + }, 10) + }) +) + +schedule( + new Promise(res => { + setTimeout(() => { + markdown("Asynchronous Markdown") + res() + }, 50) + }) +) diff --git a/source/runner/runners/_tests/_cleanDangerfile.test.ts b/source/runner/runners/_tests/_cleanDangerfile.test.ts new file mode 100644 index 000000000..045994f5e --- /dev/null +++ b/source/runner/runners/_tests/_cleanDangerfile.test.ts @@ -0,0 +1,37 @@ +import cleanDangerfile from "../utils/cleanDangerfile" + +describe("cleaning Dangerfiles", () => { + it("also handles typescript style imports", () => { + const before = ` +import { danger, warn, fail, message } from 'danger' +import { danger, warn, fail, message } from "danger" +import { danger, warn, fail, message } from "danger"; +import danger from "danger" +import danger from 'danger' +import danger from 'danger'; +` + const after = ` +// Removed import +// Removed import +// Removed import +// Removed import +// Removed import +// Removed import +` + expect(cleanDangerfile(before)).toEqual(after) + }) + + it("also handles require style imports", () => { + const before = ` +const { danger, warn, fail, message } = require('danger') +var { danger, warn, fail, message } = require("danger") +let { danger, warn, fail, message } = require('danger'); +` + const after = ` +// Removed require +// Removed require +// Removed require +` + expect(cleanDangerfile(before)).toEqual(after) + }) +}) diff --git a/source/runner/runners/_tests/_transpiler.test.ts b/source/runner/runners/_tests/_transpiler.test.ts new file mode 100644 index 000000000..3727a144e --- /dev/null +++ b/source/runner/runners/_tests/_transpiler.test.ts @@ -0,0 +1,3 @@ +it("doesn't just fail yet", () => { + expect(1).toBe(1) +}) diff --git a/source/runner/runners/inline.ts b/source/runner/runners/inline.ts new file mode 100644 index 000000000..dec95fcb8 --- /dev/null +++ b/source/runner/runners/inline.ts @@ -0,0 +1,131 @@ +import * as fs from "fs" +import * as path from "path" + +import { DangerResults } from "../../dsl/DangerResults" +import { DangerContext } from "../../runner/Dangerfile" + +import { DangerRunner } from "./runner" + +import compile from "./utils/transpiler" +import cleanDangerfile from "./utils/cleanDangerfile" +import resultsForCaughtError from "./utils/resultsForCaughtError" +var Module = require("module") + +import * as vm from "vm" + +/** + * Executes a Dangerfile at a specific path, with a context. + * The values inside a Danger context are applied as globals to the Dangerfiles runtime. + * + * @param {DangerContext} dangerfileContext the global danger context + */ +export async function createDangerfileRuntimeEnvironment(dangerfileContext: DangerContext): Promise { + return dangerfileContext +} + +// /** +// * A quick implmentation of what actually happens when require is called, but +// * without the file acccess. This comes from +// * https://stackoverflow.com/questions/17581830/load-node-js-module-from-string-in-memory#17585470 +// * +// * If this implmentation isn't enough, we can use the module require-from-string +// * +// * @param src the source code +// * @param filename the path the file represents on disk +// */ +// function requireFromString(src: string, filename: string) { +// var parent = module.parent +// var m = new Module(filename, parent) +// m.paths = Module._nodeModulePaths(path.dirname(filename)) +// m._compile(src, filename) +// return m.exports +// } + +/** + * Executes a Dangerfile at a specific path, with a context. + * The values inside a Danger context are applied as globals to the Dangerfiles runtime. + * + * @param {string} filename the file path for the dangerfile + * @param {any} environment the results of createDangerfileRuntimeEnvironment + * @returns {DangerResults} the results of the run + */ +export async function runDangerfileEnvironment( + filename: string, + originalContents: string | undefined, + environment: DangerContext +): Promise { + // We need to change the local runtime to support running JavaScript + // and TypeScript through babel first. This is a simple implmentation + // and if we need more nuance, then we can look at other implementations + + const customModuleHandler = (module: any, filename: string) => { + const contents = fs.readFileSync(filename, "utf8") + const compiled = compile(contents, filename) + module._compile(compiled, filename) + } + + // Tell all these filetypes to ge the custom compilation + require.extensions[".ts"] = customModuleHandler + require.extensions[".tsx"] = customModuleHandler + require.extensions[".js"] = customModuleHandler + require.extensions[".jsx"] = customModuleHandler + + // Require our dangerfile + originalContents = originalContents || fs.readFileSync(filename, "utf8") + let content = cleanDangerfile(originalContents) + let compiled = compile(content, filename) + + try { + // Move all the DSL attributes into the global scope + for (var key in environment) { + if (environment.hasOwnProperty(key)) { + var element = environment[key] + global[key] = element + } + } + + debugger + + // Fake a `require("dangerfile")` via the internal module API + // requireFromString(compiled, filename) + + vm.runInThisContext(compiled, { + filename: filename, + lineOffset: 0, + displayErrors: true, + }) + + // compiledWrapper.gr + + const results = environment.results + await Promise.all( + results.scheduled.map((fnOrPromise: any) => { + if (fnOrPromise instanceof Promise) { + return fnOrPromise + } + if (fnOrPromise.length === 1) { + // callback-based function + return new Promise(res => fnOrPromise(res)) + } + return fnOrPromise() + }) + ) + + return { + fails: results.fails, + warnings: results.warnings, + messages: results.messages, + markdowns: results.markdowns, + } + } catch (error) { + console.error("Unable to evaluate the Dangerfile") + return resultsForCaughtError(filename, content, error) + } +} + +const defaultExport: DangerRunner = { + createDangerfileRuntimeEnvironment, + runDangerfileEnvironment, +} + +export default defaultExport diff --git a/source/runner/runners/runner.ts b/source/runner/runners/runner.ts new file mode 100644 index 000000000..948b0d88d --- /dev/null +++ b/source/runner/runners/runner.ts @@ -0,0 +1,29 @@ +import { DangerResults } from "../../dsl/DangerResults" +import { DangerContext } from "../Dangerfile" + +export interface DangerRunner { + /** + * Executes a Dangerfile at a specific path, with a context. + * The values inside a Danger context are applied as globals to the Dangerfiles runtime. + * + * @param {string} filename the file path for the dangerfile + * @param {string | undefined} originalContents the contents of the Dangerfile, or undefined to use fs.readFileSync + * @param {any} environment the resuts of createDangerfileRuntimeEnvironment + * @returns {DangerResults} the results of the run + */ + + runDangerfileEnvironment: ( + filename: string, + originalContents: string | undefined, + environment: any + ) => Promise + + /** + * Sets up the runtime environment for running Danger, this could be loading VMs + * or creating new processes etc. The return value is expected to go into the environment + * section of runDangerfileEnvironment. + * + * @param {DangerContext} dangerfileContext the global danger context, basically the DSL + */ + createDangerfileRuntimeEnvironment: (dangerfileContext: DangerContext) => Promise +} diff --git a/source/runner/runners/utils/cleanDangerfile.ts b/source/runner/runners/utils/cleanDangerfile.ts new file mode 100644 index 000000000..c07d8653a --- /dev/null +++ b/source/runner/runners/utils/cleanDangerfile.ts @@ -0,0 +1,12 @@ +// https://regex101.com/r/dUq4yB/1 +const requirePattern = /^.* require\(('|")danger('|")\);?$/gm +// https://regex101.com/r/dUq4yB/2 +const es6Pattern = /^.* from ('|")danger('|");?$/gm + +/** + * Updates a Dangerfile to remove the import for Danger + * @param {string} contents the file path for the dangerfile + * @returns {string} the revised Dangerfile + */ +export default (contents: string): string => + contents.replace(es6Pattern, "// Removed import").replace(requirePattern, "// Removed require") diff --git a/source/runner/runners/utils/resultsForCaughtError.ts b/source/runner/runners/utils/resultsForCaughtError.ts new file mode 100644 index 000000000..67e62a547 --- /dev/null +++ b/source/runner/runners/utils/resultsForCaughtError.ts @@ -0,0 +1,28 @@ +import * as pinpoint from "pinpoint" +import { DangerResults } from "../../../dsl/DangerResults" + +/** Returns Markdown results to post if an exception is raised during the danger run */ +const resultsForCaughtError = (file: string, contents: string, error: Error): DangerResults => { + const match = /(\d+:\d+)/g.exec(error.stack!) + let code + if (match) { + const [line, column] = match[0].split(":").map(value => parseInt(value, 10) - 1) + code = pinpoint(contents, { line, column }) + } else { + code = contents + } + const failure = `Danger failed to run \`${file}\`.` + const errorMD = `## Error ${error.name} +\`\`\` +${error.message} +${error.stack} +\`\`\` +### Dangerfile +\`\`\` +${code} +\`\`\` + ` + return { fails: [{ message: failure }], warnings: [], markdowns: [errorMD], messages: [] } +} + +export default resultsForCaughtError diff --git a/source/runner/runners/utils/transpiler.ts b/source/runner/runners/utils/transpiler.ts new file mode 100644 index 000000000..160f5d3e1 --- /dev/null +++ b/source/runner/runners/utils/transpiler.ts @@ -0,0 +1,79 @@ +import * as fs from "fs" +import * as path from "path" + +let hasNativeTypeScript = false +let hasBabel = false +let hasBabelTypeScript = false +let hasFlow = false + +// Yes, lots of linter disables, but I want to support TS/Babel/Neither correclty + +try { + require.resolve("typescript") // tslint:disable-line + hasNativeTypeScript = true +} catch (e) {} // tslint:disable-line + +try { + require.resolve("babel-core") // tslint:disable-line + require("babel-polyfill") // tslint:disable-line + hasBabel = true + + try { + require.resolve("babel-plugin-transform-typescript") // tslint:disable-line + hasBabelTypeScript = true + } catch (e) {} // tslint:disable-line + + try { + require.resolve("babel-plugin-transform-flow-strip-types") // tslint:disable-line + hasFlow = true + } catch (e) {} // tslint:disable-line +} catch (e) {} // tslint:disable-line + +// Now that we have a sense of what exists inside the users' node modules + +const typescriptify = (content: string): string => { + const ts = require("typescript") // tslint:disable-line + const compilerOptions = JSON.parse(fs.readFileSync("tsconfig.json", "utf8")) + let result = ts.transpileModule(content, compilerOptions) + return result.outputText +} + +const babelify = (content: string, filename: string, extraPlugins: string[]): string => { + const babel = require("babel-core") // tslint:disable-line + if (!babel.transform) { + return content + } + + const options = babel.loadOptions ? babel.loadOptions({}) : { plugins: [] } + + const fileOpts = { + filename, + filenameRelative: filename, + sourceMap: false, + sourceFileName: null, + sourceMapTarget: null, + plugins: [...extraPlugins, ...options.plugins], + } + + const result = babel.transform(content, fileOpts) + return result.code +} + +export default (code: string, filename: string) => { + const filetype = path.extname(filename) + const isModule = filename.includes("node_modules") + if (isModule) { + return code + } + + let result = code + if (hasNativeTypeScript && filetype.startsWith(".ts")) { + result = typescriptify(code) + } else if (hasBabel && hasBabelTypeScript && filetype.startsWith(".ts")) { + result = babelify(code, filename, ["transform-typescript"]) + } else if (hasBabel && filetype.startsWith(".js")) { + result = babelify(code, filename, hasFlow ? ["transform-flow-strip-types"] : []) + } + + return result +} diff --git a/source/runner/runners/vm2.ts b/source/runner/runners/vm2.ts new file mode 100644 index 000000000..09c4e4519 --- /dev/null +++ b/source/runner/runners/vm2.ts @@ -0,0 +1,83 @@ +import { NodeVM, NodeVMOptions } from "vm2" +import * as fs from "fs" + +import { DangerContext } from "../../runner/Dangerfile" + +import compile from "./utils/transpiler" +import cleanDangerfile from "./utils/cleanDangerfile" +import resultsForCaughtError from "./utils/resultsForCaughtError" +import { DangerRunner } from "./runner" + +declare const regeneratorRuntime: any + +// A WIP version of the runner which uses a vm2 based in-process runner +// this has a few caveats ATM: +// +// * Relative files aren't getting transpiled +// * Babel sometime with async functions in the runtime + +export async function createDangerfileRuntimeEnvironment(dangerfileContext: DangerContext): Promise { + // This is for plugin support, we now have the Danger objects inside Danger's + // main process context too. This means plugins that Danger depends on can also + // get support for the globals. + + Object.keys(dangerfileContext).forEach(key => { + global[key] = dangerfileContext[key] + }) + + return { + require: { + external: true, + context: "host", + builtin: ["*"], + }, + sandbox: { ...dangerfileContext, regeneratorRuntime: regeneratorRuntime }, + compiler: compile, + } +} + +export async function runDangerfileEnvironment( + filename: string, + originalContents: string | undefined, + environment: NodeVMOptions +) { + const vm = new NodeVM(environment) + + // Require our dangerfile + originalContents = originalContents || fs.readFileSync(filename, "utf8") + let content = cleanDangerfile(originalContents) + + try { + vm.run(content, filename) + + const results = environment.sandbox!.results! + await Promise.all( + results.scheduled.map((fnOrPromise: any) => { + if (fnOrPromise instanceof Promise) { + return fnOrPromise + } + if (fnOrPromise.length === 1) { + // callback-based function + return new Promise(res => fnOrPromise(res)) + } + return fnOrPromise() + }) + ) + return { + fails: results.fails, + warnings: results.warnings, + messages: results.messages, + markdowns: results.markdowns, + } + } catch (error) { + console.error("Unable to evaluate the Dangerfile") + return resultsForCaughtError(filename, content, error) + } +} + +const defaultExport: DangerRunner = { + createDangerfileRuntimeEnvironment, + runDangerfileEnvironment, +} + +export default defaultExport diff --git a/yarn.lock b/yarn.lock index 0efe7e913..7aee35fff 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3821,6 +3821,10 @@ require-from-string@^1.1.0: version "1.2.1" resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-1.2.1.tgz#529c9ccef27380adfec9a2f965b649bbee636418" +require-from-string@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.1.tgz#c545233e9d7da6616e9d59adfb39fc9f588676ff" + require-main-filename@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" From e3815b756a7ad200f8a754c564c72cb57323765c Mon Sep 17 00:00:00 2001 From: Orta Therox Date: Sun, 24 Sep 2017 14:25:11 -0400 Subject: [PATCH 4/8] Completely switch to an eval context for the dangerfile --- package.json | 1 - source/runner/Executor.ts | 2 +- source/runner/_tests/_danger_runner.test.ts | 3 ++- .../fixtures/__DangerfileFullMessages.js | 4 ++++ source/runner/runners/inline.ts | 22 ++++--------------- yarn.lock | 4 ---- 6 files changed, 11 insertions(+), 25 deletions(-) diff --git a/package.json b/package.json index 88519a741..c274fa959 100644 --- a/package.json +++ b/package.json @@ -103,7 +103,6 @@ "parse-diff": "^0.4.0", "parse-link-header": "^1.0.1", "pinpoint": "^1.1.0", - "require-from-string": "^2.0.1", "rfc6902": "^1.3.0", "vm2": "patriksimek/vm2#custom_files", "voca": "^1.2.0" diff --git a/source/runner/Executor.ts b/source/runner/Executor.ts index fd926303e..cbaab4b8f 100644 --- a/source/runner/Executor.ts +++ b/source/runner/Executor.ts @@ -4,7 +4,7 @@ import { CISource } from "../ci_source/ci_source" import { Platform } from "../platforms/platform" import { DangerResults } from "../dsl/DangerResults" import { template as githubResultsTemplate } from "./templates/githubIssueTemplate" -import { createDangerfileRuntimeEnvironment, runDangerfileEnvironment } from "./runners/vm2" +import { createDangerfileRuntimeEnvironment, runDangerfileEnvironment } from "./runners/inline" import exceptionRaisedTemplate from "./templates/exceptionRaisedTemplate" import * as debug from "debug" diff --git a/source/runner/_tests/_danger_runner.test.ts b/source/runner/_tests/_danger_runner.test.ts index 0fb2ae050..537739562 100644 --- a/source/runner/_tests/_danger_runner.test.ts +++ b/source/runner/_tests/_danger_runner.test.ts @@ -48,6 +48,7 @@ describe("with fixtures", () => { it("handles a full set of messages", async () => { const context = await setupDangerfileContext() const runtime = await createDangerfileRuntimeEnvironment(context) + const test = () => console.log("HIYA") const results = await runDangerfileEnvironment(resolve(fixtures, "__DangerfileFullMessages.js"), undefined, runtime) expect(results).toEqual({ @@ -85,7 +86,7 @@ describe("with fixtures", () => { }) }) - it.only("handles multiple scheduled statements and all message types", async () => { + it("handles multiple scheduled statements and all message types", async () => { const context = await setupDangerfileContext() const runtime = await createDangerfileRuntimeEnvironment(context) const results = await runDangerfileEnvironment( diff --git a/source/runner/_tests/fixtures/__DangerfileFullMessages.js b/source/runner/_tests/fixtures/__DangerfileFullMessages.js index 9ed9a1064..092032b2f 100644 --- a/source/runner/_tests/fixtures/__DangerfileFullMessages.js +++ b/source/runner/_tests/fixtures/__DangerfileFullMessages.js @@ -1,5 +1,9 @@ /*eslint-disable */ +debugger + +// thing("ASDASDASD") + warn("this is a warning") message("this is a message") fail("this is a failure") diff --git a/source/runner/runners/inline.ts b/source/runner/runners/inline.ts index dec95fcb8..3ba647f41 100644 --- a/source/runner/runners/inline.ts +++ b/source/runner/runners/inline.ts @@ -1,5 +1,4 @@ import * as fs from "fs" -import * as path from "path" import { DangerResults } from "../../dsl/DangerResults" import { DangerContext } from "../../runner/Dangerfile" @@ -9,9 +8,6 @@ import { DangerRunner } from "./runner" import compile from "./utils/transpiler" import cleanDangerfile from "./utils/cleanDangerfile" import resultsForCaughtError from "./utils/resultsForCaughtError" -var Module = require("module") - -import * as vm from "vm" /** * Executes a Dangerfile at a specific path, with a context. @@ -77,25 +73,15 @@ export async function runDangerfileEnvironment( try { // Move all the DSL attributes into the global scope - for (var key in environment) { + for (let key in environment) { if (environment.hasOwnProperty(key)) { - var element = environment[key] + let element = environment[key] global[key] = element + // this[key] = element } } - debugger - - // Fake a `require("dangerfile")` via the internal module API - // requireFromString(compiled, filename) - - vm.runInThisContext(compiled, { - filename: filename, - lineOffset: 0, - displayErrors: true, - }) - - // compiledWrapper.gr + eval(compiled) const results = environment.results await Promise.all( diff --git a/yarn.lock b/yarn.lock index 7aee35fff..0efe7e913 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3821,10 +3821,6 @@ require-from-string@^1.1.0: version "1.2.1" resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-1.2.1.tgz#529c9ccef27380adfec9a2f965b649bbee636418" -require-from-string@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.1.tgz#c545233e9d7da6616e9d59adfb39fc9f588676ff" - require-main-filename@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" From a60e1715f13c33081ec8cd3b224ea718b887df23 Mon Sep 17 00:00:00 2001 From: Orta Therox Date: Sun, 24 Sep 2017 14:32:51 -0400 Subject: [PATCH 5/8] An executor now has to get a DangerRunner --- dangerfile.ts | 4 + source/commands/danger-pr.ts | 4 +- source/commands/danger-process.ts | 4 +- source/commands/danger-run.ts | 3 +- source/runner/Executor.ts | 7 +- source/runner/_tests/_danger_runner.test.ts | 100 ++++++++++++++------ source/runner/_tests/_executor.test.ts | 17 ++-- 7 files changed, 97 insertions(+), 42 deletions(-) diff --git a/dangerfile.ts b/dangerfile.ts index 5996db7e9..789356ee7 100644 --- a/dangerfile.ts +++ b/dangerfile.ts @@ -2,6 +2,10 @@ // This means we can re-use the type infra from the app, without having to // fake the import. +console.log(global) +console.log(require) +console.log(require.extensions) + import { DangerDSL } from "./source/dsl/DangerDSL" declare var danger: DangerDSL // declare var results: any diff --git a/source/commands/danger-pr.ts b/source/commands/danger-pr.ts index 3d463ac92..2e9e7354d 100644 --- a/source/commands/danger-pr.ts +++ b/source/commands/danger-pr.ts @@ -13,6 +13,8 @@ import validateDangerfileExists from "./utils/validateDangerfileExists" import openRepl from "./utils/repl" import setSharedArgs, { SharedCLI } from "./utils/sharedDangerfileArgs" +import inlineRunner from "../runner/runners/inline" + const d = debug("danger:pr") program.usage("[options] ").description("Emulate running Danger against an existing GitHub Pull Request.") @@ -51,7 +53,7 @@ async function runDanger(source: FakeCI, platform: GitHub, file: string) { verbose: app.verbose, } - const exec = new Executor(source, platform, config) + const exec = new Executor(source, platform, inlineRunner, config) const runtimeEnv = await exec.setupDanger() const results = await runDangerfileEnvironment(file, undefined, runtimeEnv) diff --git a/source/commands/danger-process.ts b/source/commands/danger-process.ts index 16fbc1545..17669b9ca 100644 --- a/source/commands/danger-process.ts +++ b/source/commands/danger-process.ts @@ -7,6 +7,8 @@ import runDangerSubprocess, { prepareDangerDSL } from "./utils/runDangerSubproce import setSharedArgs, { SharedCLI } from "./utils/sharedDangerfileArgs" import getRuntimeCISource from "./utils/getRuntimeCISource" +import inlineRunner from "../runner/runners/inline" + // Given the nature of this command, it can be tricky to test, so I use a command like this: // // env DANGER_GITHUB_API_TOKEN='xxx' DANGER_FAKE_CI="YEP" DANGER_TEST_REPO='artsy/eigen' DANGER_TEST_PR='2408' @@ -62,7 +64,7 @@ async function run() { verbose: app.verbose, } - const exec = new Executor(source, platform, config) + const exec = new Executor(source, platform, inlineRunner, config) const dangerDSL = await exec.dslForDanger() const processInput = prepareDangerDSL(dangerDSL) diff --git a/source/commands/danger-run.ts b/source/commands/danger-run.ts index 7ed373976..7b5be3737 100755 --- a/source/commands/danger-run.ts +++ b/source/commands/danger-run.ts @@ -8,6 +8,7 @@ import { dangerfilePath } from "./utils/file-utils" import setSharedArgs, { SharedCLI } from "./utils/sharedDangerfileArgs" import validateDangerfileExists from "./utils/validateDangerfileExists" import getRuntimeCISource from "./utils/getRuntimeCISource" +import inlineRunner from "../runner/runners/inline" const d = debug("danger:run") declare const global: any @@ -56,7 +57,7 @@ async function run() { verbose: app.verbose, } - const exec = new Executor(source, platform, config) + const exec = new Executor(source, platform, inlineRunner, config) exec.setupAndRunDanger(dangerFile) } } diff --git a/source/runner/Executor.ts b/source/runner/Executor.ts index cbaab4b8f..5c058715b 100644 --- a/source/runner/Executor.ts +++ b/source/runner/Executor.ts @@ -4,12 +4,12 @@ import { CISource } from "../ci_source/ci_source" import { Platform } from "../platforms/platform" import { DangerResults } from "../dsl/DangerResults" import { template as githubResultsTemplate } from "./templates/githubIssueTemplate" -import { createDangerfileRuntimeEnvironment, runDangerfileEnvironment } from "./runners/inline" import exceptionRaisedTemplate from "./templates/exceptionRaisedTemplate" import * as debug from "debug" import * as chalk from "chalk" import { sentence, href } from "./DangerUtils" +import { DangerRunner } from "./runners/runner" // This is still badly named, maybe it really should just be runner? @@ -26,6 +26,7 @@ export class Executor { constructor( public readonly ciSource: CISource, public readonly platform: Platform, + public readonly runner: DangerRunner, public readonly options: ExecutorOptions ) {} @@ -46,7 +47,7 @@ export class Executor { async setupDanger(): Promise { const dsl = await this.dslForDanger() const context = contextForDanger(dsl) - return await createDangerfileRuntimeEnvironment(context) + return await this.runner.createDangerfileRuntimeEnvironment(context) } /** @@ -61,7 +62,7 @@ export class Executor { // If an eval of the Dangerfile fails, we should generate a // message that can go back to the CI try { - results = await runDangerfileEnvironment(file, undefined, runtime) + results = await this.runner.runDangerfileEnvironment(file, undefined, runtime) } catch (error) { results = this.resultsForError(error) } diff --git a/source/runner/_tests/_danger_runner.test.ts b/source/runner/_tests/_danger_runner.test.ts index 537739562..72f7f1a45 100644 --- a/source/runner/_tests/_danger_runner.test.ts +++ b/source/runner/_tests/_danger_runner.test.ts @@ -1,5 +1,5 @@ import { contextForDanger } from "../Dangerfile" -import { createDangerfileRuntimeEnvironment, runDangerfileEnvironment } from "../runners/inline" +import inlineRunner from "../runners/inline" import { FakeCI } from "../../ci_source/providers/Fake" import { FakePlatform } from "../../platforms/FakePlatform" @@ -22,7 +22,7 @@ async function setupDangerfileContext() { verbose: false, } - const exec = new Executor(new FakeCI({}), platform, config) + const exec = new Executor(new FakeCI({}), platform, inlineRunner, config) platform.getPlatformGitRepresentation = jest.fn() platform.getPlatformDSLRepresentation = jest.fn() @@ -34,8 +34,12 @@ async function setupDangerfileContext() { describe("with fixtures", () => { it("handles a blank Dangerfile", async () => { const context = await setupDangerfileContext() - const runtime = await createDangerfileRuntimeEnvironment(context) - const results = await runDangerfileEnvironment(resolve(fixtures, "__DangerfileEmpty.js"), undefined, runtime) + const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) + const results = await inlineRunner.runDangerfileEnvironment( + resolve(fixtures, "__DangerfileEmpty.js"), + undefined, + runtime + ) expect(results).toEqual({ fails: [], @@ -47,9 +51,13 @@ describe("with fixtures", () => { it("handles a full set of messages", async () => { const context = await setupDangerfileContext() - const runtime = await createDangerfileRuntimeEnvironment(context) + const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) const test = () => console.log("HIYA") - const results = await runDangerfileEnvironment(resolve(fixtures, "__DangerfileFullMessages.js"), undefined, runtime) + const results = await inlineRunner.runDangerfileEnvironment( + resolve(fixtures, "__DangerfileFullMessages.js"), + undefined, + runtime + ) expect(results).toEqual({ fails: [{ message: "this is a failure" }], @@ -61,8 +69,12 @@ describe("with fixtures", () => { it("handles a failing dangerfile", async () => { const context = await setupDangerfileContext() - const runtime = await createDangerfileRuntimeEnvironment(context) - const results = await runDangerfileEnvironment(resolve(fixtures, "__DangerfileBadSyntax.js"), undefined, runtime) + const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) + const results = await inlineRunner.runDangerfileEnvironment( + resolve(fixtures, "__DangerfileBadSyntax.js"), + undefined, + runtime + ) expect(results.fails[0].message).toContain("Danger failed to run") expect(results.markdowns[0]).toContain("hello is not defined") @@ -70,14 +82,18 @@ describe("with fixtures", () => { it("handles relative imports correctly in Babel", async () => { const context = await setupDangerfileContext() - const runtime = await createDangerfileRuntimeEnvironment(context) - await runDangerfileEnvironment(resolve(fixtures, "__DangerfileImportRelative.js"), undefined, runtime) + const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) + await inlineRunner.runDangerfileEnvironment(resolve(fixtures, "__DangerfileImportRelative.js"), undefined, runtime) }) it("handles scheduled (async) code", async () => { const context = await setupDangerfileContext() - const runtime = await createDangerfileRuntimeEnvironment(context) - const results = await runDangerfileEnvironment(resolve(fixtures, "__DangerfileScheduled.js"), undefined, runtime) + const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) + const results = await inlineRunner.runDangerfileEnvironment( + resolve(fixtures, "__DangerfileScheduled.js"), + undefined, + runtime + ) expect(results).toEqual({ fails: [], messages: [], @@ -88,8 +104,8 @@ describe("with fixtures", () => { it("handles multiple scheduled statements and all message types", async () => { const context = await setupDangerfileContext() - const runtime = await createDangerfileRuntimeEnvironment(context) - const results = await runDangerfileEnvironment( + const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) + const results = await inlineRunner.runDangerfileEnvironment( resolve(fixtures, "__DangerfileMultiScheduled.js"), undefined, runtime @@ -104,8 +120,12 @@ describe("with fixtures", () => { it("in Typescript it handles multiple scheduled statements and all message types", async () => { const context = await setupDangerfileContext() - const runtime = await createDangerfileRuntimeEnvironment(context) - const results = await runDangerfileEnvironment(resolve(fixtures, "__DangerfileAsync.ts"), undefined, runtime) + const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) + const results = await inlineRunner.runDangerfileEnvironment( + resolve(fixtures, "__DangerfileAsync.ts"), + undefined, + runtime + ) expect(results.warnings).toEqual([ { message: "Async Function", @@ -119,8 +139,12 @@ describe("with fixtures", () => { it("in babel it can execute async/await scheduled functions", async () => { // this test takes *forever* because of babel-polyfill being required const context = await setupDangerfileContext() - const runtime = await createDangerfileRuntimeEnvironment(context) - const results = await runDangerfileEnvironment(resolve(fixtures, "__DangerfileAsync.js"), undefined, runtime) + const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) + const results = await inlineRunner.runDangerfileEnvironment( + resolve(fixtures, "__DangerfileAsync.js"), + undefined, + runtime + ) expect(results.warnings).toEqual([ { message: "Async Function", @@ -134,8 +158,12 @@ describe("with fixtures", () => { it("in typescript it can execute async/await scheduled functions", async () => { // this test takes *forever* because of babel-polyfill being required const context = await setupDangerfileContext() - const runtime = await createDangerfileRuntimeEnvironment(context) - const results = await runDangerfileEnvironment(resolve(fixtures, "__DangerfileAsync.ts"), undefined, runtime) + const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) + const results = await inlineRunner.runDangerfileEnvironment( + resolve(fixtures, "__DangerfileAsync.ts"), + undefined, + runtime + ) expect(results.warnings).toEqual([ { message: "Async Function", @@ -148,8 +176,12 @@ describe("with fixtures", () => { it("can schedule callback-based promised ", async () => { const context = await setupDangerfileContext() - const runtime = await createDangerfileRuntimeEnvironment(context) - const results = await runDangerfileEnvironment(resolve(fixtures, "__DangerfileCallback.js"), undefined, runtime) + const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) + const results = await inlineRunner.runDangerfileEnvironment( + resolve(fixtures, "__DangerfileCallback.js"), + undefined, + runtime + ) expect(results.warnings).toEqual([ { message: "Scheduled a callback", @@ -159,8 +191,12 @@ describe("with fixtures", () => { it("can handle TypeScript based Dangerfiles", async () => { const context = await setupDangerfileContext() - const runtime = await createDangerfileRuntimeEnvironment(context) - const results = await runDangerfileEnvironment(resolve(fixtures, "__DangerfileTypeScript.ts"), undefined, runtime) + const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) + const results = await inlineRunner.runDangerfileEnvironment( + resolve(fixtures, "__DangerfileTypeScript.ts"), + undefined, + runtime + ) expect(results.messages).toEqual([ { message: "Honey, we got Types", @@ -170,16 +206,24 @@ describe("with fixtures", () => { it("can handle a plugin (which is already used in Danger)", async () => { const context = await setupDangerfileContext() - const runtime = await createDangerfileRuntimeEnvironment(context) - const results = await runDangerfileEnvironment(resolve(fixtures, "__DangerfilePlugin.js"), undefined, runtime) + const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) + const results = await inlineRunner.runDangerfileEnvironment( + resolve(fixtures, "__DangerfilePlugin.js"), + undefined, + runtime + ) expect(results.fails[0].message).toContain("@types dependencies were added to package.json") }) it("does not swallow errors thrown in Dangerfile", async () => { const context = await setupDangerfileContext() - const runtime = await createDangerfileRuntimeEnvironment(context) - const results = await runDangerfileEnvironment(resolve(fixtures, "__DangerfileThrows.js"), undefined, runtime) + const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) + const results = await inlineRunner.runDangerfileEnvironment( + resolve(fixtures, "__DangerfileThrows.js"), + undefined, + runtime + ) expect(results.fails[0].message).toContain("Danger failed to run") expect(results.markdowns[0]).toContain("Error: failure") diff --git a/source/runner/_tests/_executor.test.ts b/source/runner/_tests/_executor.test.ts index 63803e006..08ecb6390 100644 --- a/source/runner/_tests/_executor.test.ts +++ b/source/runner/_tests/_executor.test.ts @@ -2,6 +2,7 @@ import { Executor } from "../Executor" import { FakeCI } from "../../ci_source/providers/Fake" import { FakePlatform } from "../../platforms/FakePlatform" import { emptyResults, warnResults, failsResults } from "./fixtures/ExampleDangerResults" +import inlineRunner from "../runners/inline" const defaultConfig = { stdoutOnly: false, @@ -11,7 +12,7 @@ const defaultConfig = { describe("setup", () => { it("gets diff / pr info in setup", async () => { const platform = new FakePlatform() - const exec = new Executor(new FakeCI({}), platform, defaultConfig) + const exec = new Executor(new FakeCI({}), platform, inlineRunner, defaultConfig) platform.getPlatformGitRepresentation = jest.fn() platform.getPlatformDSLRepresentation = jest.fn() @@ -22,7 +23,7 @@ describe("setup", () => { }) it("gets diff / pr info / utils in setup", async () => { - const exec = new Executor(new FakeCI({}), new FakePlatform(), defaultConfig) + const exec = new Executor(new FakeCI({}), new FakePlatform(), inlineRunner, defaultConfig) const dsl = await exec.dslForDanger() expect(dsl.git).toBeTruthy() expect(dsl.github).toBeTruthy() @@ -31,7 +32,7 @@ describe("setup", () => { it("Creates a DangerResults for a raising dangerfile", async () => { const platform = new FakePlatform() - const exec = new Executor(new FakeCI({}), platform, defaultConfig) + const exec = new Executor(new FakeCI({}), platform, inlineRunner, defaultConfig) // This is a real error occuring when Danger modifies the Dangerfile // as it is given a path of "" @@ -50,7 +51,7 @@ describe("setup", () => { it("Deletes a post when there are no messages", async () => { const platform = new FakePlatform() - const exec = new Executor(new FakeCI({}), platform, defaultConfig) + const exec = new Executor(new FakeCI({}), platform, inlineRunner, defaultConfig) platform.deleteMainComment = jest.fn() await exec.handleResults(emptyResults) @@ -59,7 +60,7 @@ describe("setup", () => { it("Updates or Creates comments for warnings", async () => { const platform = new FakePlatform() - const exec = new Executor(new FakeCI({}), platform, defaultConfig) + const exec = new Executor(new FakeCI({}), platform, inlineRunner, defaultConfig) platform.updateOrCreateComment = jest.fn() await exec.handleResults(warnResults) @@ -68,7 +69,7 @@ describe("setup", () => { it("Updates or Creates comments for warnings", async () => { const platform = new FakePlatform() - const exec = new Executor(new FakeCI({}), platform, defaultConfig) + const exec = new Executor(new FakeCI({}), platform, inlineRunner, defaultConfig) platform.updateOrCreateComment = jest.fn() await exec.handleResults(warnResults) @@ -77,7 +78,7 @@ describe("setup", () => { it("Updates the status with success for a passed results", async () => { const platform = new FakePlatform() - const exec = new Executor(new FakeCI({}), platform, defaultConfig) + const exec = new Executor(new FakeCI({}), platform, inlineRunner, defaultConfig) platform.updateOrCreateComment = jest.fn() platform.updateStatus = jest.fn() @@ -90,7 +91,7 @@ describe("setup", () => { it("Updates the status with success for a passed results", async () => { const platform = new FakePlatform() - const exec = new Executor(new FakeCI({}), platform, defaultConfig) + const exec = new Executor(new FakeCI({}), platform, inlineRunner, defaultConfig) platform.updateOrCreateComment = jest.fn() platform.updateStatus = jest.fn() From a9536b3a81802af6849d3eec8354caacc94452fe Mon Sep 17 00:00:00 2001 From: Orta Therox Date: Sat, 14 Oct 2017 09:49:19 -0400 Subject: [PATCH 6/8] Fix tslint, add more tests to the runner integration: --- package.json | 2 +- source/runner/_tests/_danger_runner.test.ts | 231 ----------------- .../_tests/_runner_integration.test.ts | 240 ++++++++++++++++++ source/runner/runners/runner.ts | 29 +-- yarn.lock | 90 ++++--- 5 files changed, 304 insertions(+), 288 deletions(-) delete mode 100644 source/runner/_tests/_danger_runner.test.ts create mode 100644 source/runner/runners/_tests/_runner_integration.test.ts diff --git a/package.json b/package.json index c274fa959..d9db03784 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "jest": "21", "lint-staged": "^4.0.0", "madge": "^2.0.0", - "prettier": "^1.5.3", + "prettier": "^1.7.4", "shx": "^0.2.1", "ts-jest": "^21", "ts-node": "^3.2.1", diff --git a/source/runner/_tests/_danger_runner.test.ts b/source/runner/_tests/_danger_runner.test.ts deleted file mode 100644 index 72f7f1a45..000000000 --- a/source/runner/_tests/_danger_runner.test.ts +++ /dev/null @@ -1,231 +0,0 @@ -import { contextForDanger } from "../Dangerfile" -import inlineRunner from "../runners/inline" - -import { FakeCI } from "../../ci_source/providers/Fake" -import { FakePlatform } from "../../platforms/FakePlatform" -import { Executor } from "../Executor" - -import * as os from "os" -import * as fs from "fs" - -import { resolve } from "path" -const fixtures = resolve(__dirname, "fixtures") - -/** - * Sets up an example context - * @returns {Promise} a context - */ -async function setupDangerfileContext() { - const platform = new FakePlatform() - const config = { - stdoutOnly: false, - verbose: false, - } - - const exec = new Executor(new FakeCI({}), platform, inlineRunner, config) - - platform.getPlatformGitRepresentation = jest.fn() - platform.getPlatformDSLRepresentation = jest.fn() - - const dsl = await exec.dslForDanger() - return contextForDanger(dsl) -} - -describe("with fixtures", () => { - it("handles a blank Dangerfile", async () => { - const context = await setupDangerfileContext() - const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) - const results = await inlineRunner.runDangerfileEnvironment( - resolve(fixtures, "__DangerfileEmpty.js"), - undefined, - runtime - ) - - expect(results).toEqual({ - fails: [], - markdowns: [], - messages: [], - warnings: [], - }) - }) - - it("handles a full set of messages", async () => { - const context = await setupDangerfileContext() - const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) - const test = () => console.log("HIYA") - const results = await inlineRunner.runDangerfileEnvironment( - resolve(fixtures, "__DangerfileFullMessages.js"), - undefined, - runtime - ) - - expect(results).toEqual({ - fails: [{ message: "this is a failure" }], - markdowns: ["this is a *markdown*"], - messages: [{ message: "this is a message" }], - warnings: [{ message: "this is a warning" }], - }) - }) - - it("handles a failing dangerfile", async () => { - const context = await setupDangerfileContext() - const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) - const results = await inlineRunner.runDangerfileEnvironment( - resolve(fixtures, "__DangerfileBadSyntax.js"), - undefined, - runtime - ) - - expect(results.fails[0].message).toContain("Danger failed to run") - expect(results.markdowns[0]).toContain("hello is not defined") - }) - - it("handles relative imports correctly in Babel", async () => { - const context = await setupDangerfileContext() - const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) - await inlineRunner.runDangerfileEnvironment(resolve(fixtures, "__DangerfileImportRelative.js"), undefined, runtime) - }) - - it("handles scheduled (async) code", async () => { - const context = await setupDangerfileContext() - const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) - const results = await inlineRunner.runDangerfileEnvironment( - resolve(fixtures, "__DangerfileScheduled.js"), - undefined, - runtime - ) - expect(results).toEqual({ - fails: [], - messages: [], - markdowns: [], - warnings: [{ message: "Asynchronous Warning" }], - }) - }) - - it("handles multiple scheduled statements and all message types", async () => { - const context = await setupDangerfileContext() - const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) - const results = await inlineRunner.runDangerfileEnvironment( - resolve(fixtures, "__DangerfileMultiScheduled.js"), - undefined, - runtime - ) - expect(results).toEqual({ - fails: [{ message: "Asynchronous Failure" }], - messages: [{ message: "Asynchronous Message" }], - markdowns: ["Asynchronous Markdown"], - warnings: [{ message: "Asynchronous Warning" }], - }) - }) - - it("in Typescript it handles multiple scheduled statements and all message types", async () => { - const context = await setupDangerfileContext() - const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) - const results = await inlineRunner.runDangerfileEnvironment( - resolve(fixtures, "__DangerfileAsync.ts"), - undefined, - runtime - ) - expect(results.warnings).toEqual([ - { - message: "Async Function", - }, - { - message: "After Async Function", - }, - ]) - }) - - it("in babel it can execute async/await scheduled functions", async () => { - // this test takes *forever* because of babel-polyfill being required - const context = await setupDangerfileContext() - const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) - const results = await inlineRunner.runDangerfileEnvironment( - resolve(fixtures, "__DangerfileAsync.js"), - undefined, - runtime - ) - expect(results.warnings).toEqual([ - { - message: "Async Function", - }, - { - message: "After Async Function", - }, - ]) - }) - - it("in typescript it can execute async/await scheduled functions", async () => { - // this test takes *forever* because of babel-polyfill being required - const context = await setupDangerfileContext() - const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) - const results = await inlineRunner.runDangerfileEnvironment( - resolve(fixtures, "__DangerfileAsync.ts"), - undefined, - runtime - ) - expect(results.warnings).toEqual([ - { - message: "Async Function", - }, - { - message: "After Async Function", - }, - ]) - }) - - it("can schedule callback-based promised ", async () => { - const context = await setupDangerfileContext() - const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) - const results = await inlineRunner.runDangerfileEnvironment( - resolve(fixtures, "__DangerfileCallback.js"), - undefined, - runtime - ) - expect(results.warnings).toEqual([ - { - message: "Scheduled a callback", - }, - ]) - }) - - it("can handle TypeScript based Dangerfiles", async () => { - const context = await setupDangerfileContext() - const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) - const results = await inlineRunner.runDangerfileEnvironment( - resolve(fixtures, "__DangerfileTypeScript.ts"), - undefined, - runtime - ) - expect(results.messages).toEqual([ - { - message: "Honey, we got Types", - }, - ]) - }) - - it("can handle a plugin (which is already used in Danger)", async () => { - const context = await setupDangerfileContext() - const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) - const results = await inlineRunner.runDangerfileEnvironment( - resolve(fixtures, "__DangerfilePlugin.js"), - undefined, - runtime - ) - - expect(results.fails[0].message).toContain("@types dependencies were added to package.json") - }) - - it("does not swallow errors thrown in Dangerfile", async () => { - const context = await setupDangerfileContext() - const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) - const results = await inlineRunner.runDangerfileEnvironment( - resolve(fixtures, "__DangerfileThrows.js"), - undefined, - runtime - ) - - expect(results.fails[0].message).toContain("Danger failed to run") - expect(results.markdowns[0]).toContain("Error: failure") - }) -}) diff --git a/source/runner/runners/_tests/_runner_integration.test.ts b/source/runner/runners/_tests/_runner_integration.test.ts new file mode 100644 index 000000000..da5c91b98 --- /dev/null +++ b/source/runner/runners/_tests/_runner_integration.test.ts @@ -0,0 +1,240 @@ +import { contextForDanger } from "../../Dangerfile" +import inlineRunner from "../inline" +import vm2 from "../vm2" + +import { FakeCI } from "../../../ci_source/providers/Fake" +import { FakePlatform } from "../../../platforms/FakePlatform" +import { Executor } from "../../Executor" + +import * as os from "os" +import * as fs from "fs" + +import { resolve } from "path" +const lettures = resolve(__dirname, "../../_tests/fixtures") + +const letners = [{ name: "inline", fn: inlineRunner }, { name: "vm2", fn: vm2 }] + +runners.forEach(runner => { + describe(runner.name, () => { + /** + * Sets up an example context + * @returns {Promise} a context + */ + async function setupDangerfileContext() { + const lettform = new FakePlatform() + const letfig = { + stdoutOnly: false, + verbose: false, + } + + const letc = new Executor(new FakeCI({}), platform, runner.fn, config) + + platform.getPlatformGitRepresentation = jest.fn() + platform.getPlatformDSLRepresentation = jest.fn() + + const let = await exec.dslForDanger() + return contextForDanger(dsl) + } + + it("handles a blank Dangerfile", async () => { + const lettext = await setupDangerfileContext() + const lettime = await inlineRunner.createDangerfileRuntimeEnvironment(context) + const letults = await inlineRunner.runDangerfileEnvironment( + resolve(fixtures, "__DangerfileEmpty.js"), + undefined, + runtime + ) + + expect(results).toEqual({ + fails: [], + markdowns: [], + messages: [], + warnings: [], + }) + }) + + it("handles a full set of messages", async () => { + const lettext = await setupDangerfileContext() + const lettime = await inlineRunner.createDangerfileRuntimeEnvironment(context) + const lett = () => console.log("HIYA") + const letults = await inlineRunner.runDangerfileEnvironment( + resolve(fixtures, "__DangerfileFullMessages.js"), + undefined, + runtime + ) + + expect(results).toEqual({ + fails: [{ message: "this is a failure" }], + markdowns: ["this is a *markdown*"], + messages: [{ message: "this is a message" }], + warnings: [{ message: "this is a warning" }], + }) + }) + + it("handles a failing dangerfile", async () => { + const lettext = await setupDangerfileContext() + const lettime = await inlineRunner.createDangerfileRuntimeEnvironment(context) + const letults = await inlineRunner.runDangerfileEnvironment( + resolve(fixtures, "__DangerfileBadSyntax.js"), + undefined, + runtime + ) + + expect(results.fails[0].message).toContain("Danger failed to run") + expect(results.markdowns[0]).toContain("hello is not defined") + }) + + it("handles relative imports correctly in Babel", async () => { + const lettext = await setupDangerfileContext() + const lettime = await inlineRunner.createDangerfileRuntimeEnvironment(context) + await inlineRunner.runDangerfileEnvironment( + resolve(fixtures, "__DangerfileImportRelative.js"), + undefined, + runtime + ) + }) + + it("handles scheduled (async) code", async () => { + const lettext = await setupDangerfileContext() + const lettime = await inlineRunner.createDangerfileRuntimeEnvironment(context) + const letults = await inlineRunner.runDangerfileEnvironment( + resolve(fixtures, "__DangerfileScheduled.js"), + undefined, + runtime + ) + expect(results).toEqual({ + fails: [], + messages: [], + markdowns: [], + warnings: [{ message: "Asynchronous Warning" }], + }) + }) + + it("handles multiple scheduled statements and all message types", async () => { + const lettext = await setupDangerfileContext() + const lettime = await inlineRunner.createDangerfileRuntimeEnvironment(context) + const letults = await inlineRunner.runDangerfileEnvironment( + resolve(fixtures, "__DangerfileMultiScheduled.js"), + undefined, + runtime + ) + expect(results).toEqual({ + fails: [{ message: "Asynchronous Failure" }], + messages: [{ message: "Asynchronous Message" }], + markdowns: ["Asynchronous Markdown"], + warnings: [{ message: "Asynchronous Warning" }], + }) + }) + + it("in Typescript it handles multiple scheduled statements and all message types", async () => { + const lettext = await setupDangerfileContext() + const lettime = await inlineRunner.createDangerfileRuntimeEnvironment(context) + const letults = await inlineRunner.runDangerfileEnvironment( + resolve(fixtures, "__DangerfileAsync.ts"), + undefined, + runtime + ) + expect(results.warnings).toEqual([ + { + message: "Async Function", + }, + { + message: "After Async Function", + }, + ]) + }) + + it("in babel it can execute async/await scheduled functions", async () => { + // this test takes *forever* because of babel-polyfill being required + const lettext = await setupDangerfileContext() + const lettime = await inlineRunner.createDangerfileRuntimeEnvironment(context) + const letults = await inlineRunner.runDangerfileEnvironment( + resolve(fixtures, "__DangerfileAsync.js"), + undefined, + runtime + ) + expect(results.warnings).toEqual([ + { + message: "Async Function", + }, + { + message: "After Async Function", + }, + ]) + }) + + it("in typescript it can execute async/await scheduled functions", async () => { + // this test takes *forever* because of babel-polyfill being required + const lettext = await setupDangerfileContext() + const lettime = await inlineRunner.createDangerfileRuntimeEnvironment(context) + const letults = await inlineRunner.runDangerfileEnvironment( + resolve(fixtures, "__DangerfileAsync.ts"), + undefined, + runtime + ) + expect(results.warnings).toEqual([ + { + message: "Async Function", + }, + { + message: "After Async Function", + }, + ]) + }) + + it("can schedule callback-based promised ", async () => { + const lettext = await setupDangerfileContext() + const lettime = await inlineRunner.createDangerfileRuntimeEnvironment(context) + const letults = await inlineRunner.runDangerfileEnvironment( + resolve(fixtures, "__DangerfileCallback.js"), + undefined, + runtime + ) + expect(results.warnings).toEqual([ + { + message: "Scheduled a callback", + }, + ]) + }) + + it("can handle TypeScript based Dangerfiles", async () => { + const lettext = await setupDangerfileContext() + const lettime = await inlineRunner.createDangerfileRuntimeEnvironment(context) + const letults = await inlineRunner.runDangerfileEnvironment( + resolve(fixtures, "__DangerfileTypeScript.ts"), + undefined, + runtime + ) + expect(results.messages).toEqual([ + { + message: "Honey, we got Types", + }, + ]) + }) + + it("can handle a plugin (which is already used in Danger)", async () => { + const lettext = await setupDangerfileContext() + const lettime = await inlineRunner.createDangerfileRuntimeEnvironment(context) + const letults = await inlineRunner.runDangerfileEnvironment( + resolve(fixtures, "__DangerfilePlugin.js"), + undefined, + runtime + ) + + expect(results.fails[0].message).toContain("@types dependencies were added to package.json") + }) + + it("does not swallow errors thrown in Dangerfile", async () => { + const lettext = await setupDangerfileContext() + const lettime = await inlineRunner.createDangerfileRuntimeEnvironment(context) + const letults = await inlineRunner.runDangerfileEnvironment( + resolve(fixtures, "__DangerfileThrows.js"), + undefined, + runtime + ) + + expect(results.fails[0].message).toContain("Danger failed to run") + expect(results.markdowns[0]).toContain("Error: failure") + }) + }) +}) diff --git a/source/runner/runners/runner.ts b/source/runner/runners/runner.ts index 948b0d88d..64f60a1dd 100644 --- a/source/runner/runners/runner.ts +++ b/source/runner/runners/runner.ts @@ -3,15 +3,14 @@ import { DangerContext } from "../Dangerfile" export interface DangerRunner { /** - * Executes a Dangerfile at a specific path, with a context. - * The values inside a Danger context are applied as globals to the Dangerfiles runtime. - * - * @param {string} filename the file path for the dangerfile - * @param {string | undefined} originalContents the contents of the Dangerfile, or undefined to use fs.readFileSync - * @param {any} environment the resuts of createDangerfileRuntimeEnvironment - * @returns {DangerResults} the results of the run - */ - + * Executes a Dangerfile at a specific path, with a context. + * The values inside a Danger context are applied as globals to the Dangerfiles runtime. + * + * @param {string} filename the file path for the dangerfile + * @param {string | undefined} originalContents the contents of the Dangerfile, or undefined to use fs.readFileSync + * @param {any} environment the resuts of createDangerfileRuntimeEnvironment + * @returns {DangerResults} the results of the run + */ runDangerfileEnvironment: ( filename: string, originalContents: string | undefined, @@ -19,11 +18,11 @@ export interface DangerRunner { ) => Promise /** - * Sets up the runtime environment for running Danger, this could be loading VMs - * or creating new processes etc. The return value is expected to go into the environment - * section of runDangerfileEnvironment. - * - * @param {DangerContext} dangerfileContext the global danger context, basically the DSL - */ + * Sets up the runtime environment for running Danger, this could be loading VMs + * or creating new processes etc. The return value is expected to go into the environment + * section of runDangerfileEnvironment. + * + * @param {DangerContext} dangerfileContext the global danger context, basically the DSL + */ createDangerfileRuntimeEnvironment: (dangerfileContext: DangerContext) => Promise } diff --git a/yarn.lock b/yarn.lock index 0efe7e913..4d3af5bcd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -257,7 +257,15 @@ babel-code-frame@7.0.0-alpha.19: esutils "^2.0.2" js-tokens "^3.0.0" -babel-code-frame@^6.22.0, babel-code-frame@^6.8.0: +babel-code-frame@^6.22.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" + dependencies: + chalk "^1.1.3" + esutils "^2.0.2" + js-tokens "^3.0.2" + +babel-code-frame@^6.8.0: version "6.22.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.22.0.tgz#027620bee567a88c32561574e7fd0801d33118e4" dependencies: @@ -913,10 +921,6 @@ babylon@~6.8.1: dependencies: babel-runtime "^6.0.0" -balanced-match@^0.4.1: - version "0.4.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" - balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" @@ -947,14 +951,7 @@ boom@2.x.x: dependencies: hoek "2.x.x" -brace-expansion@^1.0.0: - version "1.1.6" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.6.tgz#7197d7eaa9b87e648390ea61fc66c84427420df9" - dependencies: - balanced-match "^0.4.1" - concat-map "0.0.1" - -brace-expansion@^1.1.7: +brace-expansion@^1.0.0, brace-expansion@^1.1.7: version "1.1.8" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" dependencies: @@ -1428,10 +1425,14 @@ detective-stylus@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/detective-stylus/-/detective-stylus-1.0.0.tgz#50aee7db8babb990381f010c63fabba5b58e54cd" -diff@^3.1.0, diff@^3.2.0: +diff@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9" +diff@^3.2.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.4.0.tgz#b1d85507daf3964828de54b37d0d73ba67dda56c" + dom-serializer@0, dom-serializer@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" @@ -1926,7 +1927,7 @@ glob-parent@^2.0.0: dependencies: is-glob "^2.0.0" -glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1: +glob@^7.0.0, glob@^7.0.3, glob@^7.0.5: version "7.1.1" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" dependencies: @@ -1937,7 +1938,7 @@ glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.1.2, glob@~7.1.2: +glob@^7.1.1, glob@^7.1.2, glob@~7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" dependencies: @@ -2616,9 +2617,9 @@ jodid25519@^1.0.0: dependencies: jsbn "~0.1.0" -js-tokens@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7" +js-tokens@^3.0.0, js-tokens@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" js-yaml@^3.4.3, js-yaml@^3.7.0: version "3.8.2" @@ -3068,19 +3069,19 @@ mimic-fn@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18" -minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3: +minimatch@^3.0.0, minimatch@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774" dependencies: brace-expansion "^1.0.0" -minimatch@^3.0.4: +minimatch@^3.0.2, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" dependencies: brace-expansion "^1.1.7" -minimist@0.0.8, minimist@~0.0.1: +minimist@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" @@ -3092,6 +3093,10 @@ minimist@1.2.0, minimist@^1.1.0, minimist@^1.1.1, minimist@^1.2.0, minimist@~1.2 version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" +minimist@~0.0.1: + version "0.0.10" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" + mkdirp@0.3.x: version "0.3.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.5.tgz#de3e5f8961c88c787ee1368df849ac4413eca8d7" @@ -3334,7 +3339,7 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" -optimist@^0.6.1, optimist@~0.6.0: +optimist@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" dependencies: @@ -3568,9 +3573,9 @@ preserve@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" -prettier@^1.5.3: - version "1.7.0" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.7.0.tgz#47481588f41f7c90f63938feb202ac82554e7150" +prettier@^1.7.4: + version "1.7.4" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.7.4.tgz#5e8624ae9363c80f95ec644584ecdf55d74f93fa" pretty-format@^21.1.0: version "21.1.0" @@ -3845,13 +3850,13 @@ resolve@1.1.7, resolve@~1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" -resolve@^1.1.6, resolve@^1.3.2: +resolve@^1.1.6: version "1.3.2" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.3.2.tgz#1f0442c9e0cbb8136e87b9305f932f46c7f28235" dependencies: path-parse "^1.0.5" -resolve@~1.4.0: +resolve@^1.3.2, resolve@~1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.4.0.tgz#a75be01c53da25d934a98ebd0e4c4a7312f92a86" dependencies: @@ -3932,11 +3937,11 @@ sax@^1.1.4, sax@^1.2.1: version "1.2.2" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.2.tgz#fd8631a23bc7826bef5d871bdb87378c95647828" -"semver@2 || 3 || 4 || 5", semver@^5.3.0: +"semver@2 || 3 || 4 || 5": version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" -semver@^5.4.1: +semver@^5.3.0, semver@^5.4.1: version "5.4.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" @@ -4327,31 +4332,34 @@ tsconfig@^6.0.0: strip-bom "^3.0.0" strip-json-comments "^2.0.0" -tslib@^1.6.0: - version "1.7.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.7.1.tgz#bc8004164691923a79fe8378bbeb3da2017538ec" +tslib@^1.7.1: + version "1.8.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.8.0.tgz#dc604ebad64bcbf696d613da6c954aa0e7ea1eb6" tslint-config-prettier@^1.0.0: version "1.5.0" resolved "https://registry.yarnpkg.com/tslint-config-prettier/-/tslint-config-prettier-1.5.0.tgz#76645425edcc34d9b6835ba58266eaf90fdbfeda" tslint@^5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.3.0.tgz#fb89e4e9c461a2dcb2a80ece8da27f5cd44eff8f" + version "5.7.0" + resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.7.0.tgz#c25e0d0c92fa1201c2bc30e844e08e682b4f3552" dependencies: babel-code-frame "^6.22.0" colors "^1.1.2" + commander "^2.9.0" diff "^3.2.0" glob "^7.1.1" - optimist "~0.6.0" + minimatch "^3.0.4" resolve "^1.3.2" semver "^5.3.0" - tslib "^1.6.0" - tsutils "^2.0.0" + tslib "^1.7.1" + tsutils "^2.8.1" -tsutils@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.0.0.tgz#ae0ca48a4b431b5c04e4f711a9227aa2e37e2e04" +tsutils@^2.8.1: + version "2.12.1" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.12.1.tgz#f4d95ce3391c8971e46e54c4cf0edb0a21dd5b24" + dependencies: + tslib "^1.7.1" tunnel-agent@^0.6.0: version "0.6.0" From 21ce74df81e8d042e3f91e99e804fa4934c3a579 Mon Sep 17 00:00:00 2001 From: Orta Therox Date: Sat, 14 Oct 2017 17:12:17 -0400 Subject: [PATCH 7/8] Try use requier from string to eval inside the inline runner --- dangerfile.ts | 8 +++++--- package.json | 1 + source/ambient.d.ts | 1 + source/runner/runners/inline.ts | 9 ++++++++- yarn.lock | 4 ++++ 5 files changed, 19 insertions(+), 4 deletions(-) diff --git a/dangerfile.ts b/dangerfile.ts index 789356ee7..14b81695e 100644 --- a/dangerfile.ts +++ b/dangerfile.ts @@ -2,9 +2,9 @@ // This means we can re-use the type infra from the app, without having to // fake the import. -console.log(global) -console.log(require) -console.log(require.extensions) +// console.log(global) +// console.log(require) +// console.log(require.extensions) import { DangerDSL } from "./source/dsl/DangerDSL" declare var danger: DangerDSL @@ -46,6 +46,8 @@ schedule(yarn()) // This also serves as the "one true DSL" for a Danger run against a PR // which tools can then work against. +// debugger + import dtsGenerator from "./scripts/danger-dts" const currentDTS = dtsGenerator() const savedDTS = fs.readFileSync("source/danger.d.ts").toString() diff --git a/package.json b/package.json index d9db03784..72d027e8b 100644 --- a/package.json +++ b/package.json @@ -103,6 +103,7 @@ "parse-diff": "^0.4.0", "parse-link-header": "^1.0.1", "pinpoint": "^1.1.0", + "require-from-string": "^2.0.1", "rfc6902": "^1.3.0", "vm2": "patriksimek/vm2#custom_files", "voca": "^1.2.0" diff --git a/source/ambient.d.ts b/source/ambient.d.ts index 4db69d35a..61ca39055 100644 --- a/source/ambient.d.ts +++ b/source/ambient.d.ts @@ -16,6 +16,7 @@ declare module "pinpoint" declare module "*/package.json" declare module "require-from-string" +declare module "node-eval" // declare module "require-from-string" { // export interface RequireOptions { diff --git a/source/runner/runners/inline.ts b/source/runner/runners/inline.ts index 3ba647f41..1db30f48c 100644 --- a/source/runner/runners/inline.ts +++ b/source/runner/runners/inline.ts @@ -1,5 +1,7 @@ import * as fs from "fs" +import * as _require from "require-from-string" + import { DangerResults } from "../../dsl/DangerResults" import { DangerContext } from "../../runner/Dangerfile" @@ -80,8 +82,13 @@ export async function runDangerfileEnvironment( // this[key] = element } } + // const myCWD = path.dirname(process.cwd()) + // process.chdir(path.dirname(filename)) + // eval(compiled) + // process.chdir(myCWD) - eval(compiled) + // _eval(compiled, filename, environment, true) + _require(compiled, filename, {}) const results = environment.results await Promise.all( diff --git a/yarn.lock b/yarn.lock index 4d3af5bcd..f63448483 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3826,6 +3826,10 @@ require-from-string@^1.1.0: version "1.2.1" resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-1.2.1.tgz#529c9ccef27380adfec9a2f965b649bbee636418" +require-from-string@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.1.tgz#c545233e9d7da6616e9d59adfb39fc9f588676ff" + require-main-filename@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" From 3f3bf2617ae8d12877a9f2c9d1b9eb332dab3236 Mon Sep 17 00:00:00 2001 From: Orta Therox Date: Sat, 14 Oct 2017 20:00:38 -0400 Subject: [PATCH 8/8] Honestly, just kinda tired of looking at module resolution information --- .travis.yml | 12 ++- .../_tests/_runner_integration.test.ts | 81 ++++++++++++------- .../runner/runners/_tests/_transpiler.test.ts | 3 - source/runner/runners/utils/transpiler.ts | 5 +- 4 files changed, 62 insertions(+), 39 deletions(-) delete mode 100644 source/runner/runners/_tests/_transpiler.test.ts diff --git a/.travis.yml b/.travis.yml index 32eb70154..cd3320f50 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,20 +7,25 @@ cache: matrix: include: + # Some normal CI test runs + - node_js: '8' + - node_js: '6' + + # Does the real danger run - node_js: node - before_install: - - yarn global add greenkeeper-lockfile@1 before_script: - greenkeeper-lockfile-update - yarn run link script: + - echo "This is only for running the real danger on this repo" - danger run --verbose after_script: - greenkeeper-lockfile-upload - - node_js: '6' + # Create some fake projects at runtime - node_js: '7' script: + - echo "This is only for Integration tests on two blank projects" - yarn build - mkdir danger_blank_test - cd danger_blank_test @@ -42,7 +47,6 @@ matrix: - cd .. - rm -rf danger_babel_test - - node_js: '8' script: - yarn lint diff --git a/source/runner/runners/_tests/_runner_integration.test.ts b/source/runner/runners/_tests/_runner_integration.test.ts index f12951dda..659eaa506 100644 --- a/source/runner/runners/_tests/_runner_integration.test.ts +++ b/source/runner/runners/_tests/_runner_integration.test.ts @@ -12,10 +12,26 @@ import * as fs from "fs" import { resolve } from "path" const fixtures = resolve(__dirname, "../../_tests/fixtures") -const runners = [{ name: "inline", fn: inlineRunner }, { name: "vm2", fn: vm2 }] +// const runners = [{ name: "inline", fn: inlineRunner }, { name: "vm2", fn: vm2 }] +const runners = [{ name: "vm2", fn: vm2 }] +// const runners = [{ name: "inline", fn: inlineRunner }] runners.forEach(run => { describe(run.name, () => { + const makeExecutor = () => { + const platform = new FakePlatform() + const config = { + stdoutOnly: false, + verbose: false, + } + + exec = new Executor(new FakeCI({}), platform, run.fn, config) + + platform.getPlatformGitRepresentation = jest.fn() + platform.getPlatformDSLRepresentation = jest.fn() + return exec + } + /** * Sets up an example context * @returns {Promise} a context @@ -27,7 +43,7 @@ runners.forEach(run => { verbose: false, } - const exec = new Executor(new FakeCI({}), platform, run.fn, config) + exec = new Executor(new FakeCI({}), platform, run.fn, config) platform.getPlatformGitRepresentation = jest.fn() platform.getPlatformDSLRepresentation = jest.fn() @@ -36,11 +52,13 @@ runners.forEach(run => { return contextForDanger(dsl) } + let exec: Executor + describe("with fixtures", () => { it("handles a blank Dangerfile", async () => { const context = await setupDangerfileContext() - const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) - const results = await inlineRunner.runDangerfileEnvironment( + const runtime = await exec.runner.createDangerfileRuntimeEnvironment(context) + const results = await exec.runner.runDangerfileEnvironment( resolve(fixtures, "__DangerfileEmpty.js"), undefined, runtime @@ -55,10 +73,13 @@ runners.forEach(run => { }) it("handles a full set of messages", async () => { - const context = await setupDangerfileContext() - const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) - const test = () => console.log("HIYA") - const results = await inlineRunner.runDangerfileEnvironment( + const exec = makeExecutor() + + const dsl = await exec.dslForDanger() + const context = await contextForDanger(dsl) + const runtime = await exec.runner.createDangerfileRuntimeEnvironment(context) + + const results = await exec.runner.runDangerfileEnvironment( resolve(fixtures, "__DangerfileFullMessages.js"), undefined, runtime @@ -74,8 +95,8 @@ runners.forEach(run => { it("handles a failing dangerfile", async () => { const context = await setupDangerfileContext() - const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) - const results = await inlineRunner.runDangerfileEnvironment( + const runtime = await exec.runner.createDangerfileRuntimeEnvironment(context) + const results = await exec.runner.runDangerfileEnvironment( resolve(fixtures, "__DangerfileBadSyntax.js"), undefined, runtime @@ -87,8 +108,8 @@ runners.forEach(run => { it("handles relative imports correctly in Babel", async () => { const context = await setupDangerfileContext() - const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) - await inlineRunner.runDangerfileEnvironment( + const runtime = await exec.runner.createDangerfileRuntimeEnvironment(context) + await exec.runner.runDangerfileEnvironment( resolve(fixtures, "__DangerfileImportRelative.js"), undefined, runtime @@ -97,8 +118,8 @@ runners.forEach(run => { it("handles scheduled (async) code", async () => { const context = await setupDangerfileContext() - const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) - const results = await inlineRunner.runDangerfileEnvironment( + const runtime = await exec.runner.createDangerfileRuntimeEnvironment(context) + const results = await exec.runner.runDangerfileEnvironment( resolve(fixtures, "__DangerfileScheduled.js"), undefined, runtime @@ -113,8 +134,8 @@ runners.forEach(run => { it("handles multiple scheduled statements and all message types", async () => { const context = await setupDangerfileContext() - const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) - const results = await inlineRunner.runDangerfileEnvironment( + const runtime = await exec.runner.createDangerfileRuntimeEnvironment(context) + const results = await exec.runner.runDangerfileEnvironment( resolve(fixtures, "__DangerfileMultiScheduled.js"), undefined, runtime @@ -129,8 +150,8 @@ runners.forEach(run => { it("in Typescript it handles multiple scheduled statements and all message types", async () => { const context = await setupDangerfileContext() - const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) - const results = await inlineRunner.runDangerfileEnvironment( + const runtime = await exec.runner.createDangerfileRuntimeEnvironment(context) + const results = await exec.runner.runDangerfileEnvironment( resolve(fixtures, "__DangerfileAsync.ts"), undefined, runtime @@ -148,8 +169,8 @@ runners.forEach(run => { it("in babel it can execute async/await scheduled functions", async () => { // this test takes *forever* because of babel-polyfill being required const context = await setupDangerfileContext() - const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) - const results = await inlineRunner.runDangerfileEnvironment( + const runtime = await exec.runner.createDangerfileRuntimeEnvironment(context) + const results = await exec.runner.runDangerfileEnvironment( resolve(fixtures, "__DangerfileAsync.js"), undefined, runtime @@ -167,8 +188,8 @@ runners.forEach(run => { it("in typescript it can execute async/await scheduled functions", async () => { // this test takes *forever* because of babel-polyfill being required const context = await setupDangerfileContext() - const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) - const results = await inlineRunner.runDangerfileEnvironment( + const runtime = await exec.runner.createDangerfileRuntimeEnvironment(context) + const results = await exec.runner.runDangerfileEnvironment( resolve(fixtures, "__DangerfileAsync.ts"), undefined, runtime @@ -185,8 +206,8 @@ runners.forEach(run => { it("can schedule callback-based promised ", async () => { const context = await setupDangerfileContext() - const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) - const results = await inlineRunner.runDangerfileEnvironment( + const runtime = await exec.runner.createDangerfileRuntimeEnvironment(context) + const results = await exec.runner.runDangerfileEnvironment( resolve(fixtures, "__DangerfileCallback.js"), undefined, runtime @@ -200,8 +221,8 @@ runners.forEach(run => { it("can handle TypeScript based Dangerfiles", async () => { const context = await setupDangerfileContext() - const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) - const results = await inlineRunner.runDangerfileEnvironment( + const runtime = await exec.runner.createDangerfileRuntimeEnvironment(context) + const results = await exec.runner.runDangerfileEnvironment( resolve(fixtures, "__DangerfileTypeScript.ts"), undefined, runtime @@ -215,8 +236,8 @@ runners.forEach(run => { it("can handle a plugin (which is already used in Danger)", async () => { const context = await setupDangerfileContext() - const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) - const results = await inlineRunner.runDangerfileEnvironment( + const runtime = await exec.runner.createDangerfileRuntimeEnvironment(context) + const results = await exec.runner.runDangerfileEnvironment( resolve(fixtures, "__DangerfilePlugin.js"), undefined, runtime @@ -227,8 +248,8 @@ runners.forEach(run => { it("does not swallow errors thrown in Dangerfile", async () => { const context = await setupDangerfileContext() - const runtime = await inlineRunner.createDangerfileRuntimeEnvironment(context) - const results = await inlineRunner.runDangerfileEnvironment( + const runtime = await exec.runner.createDangerfileRuntimeEnvironment(context) + const results = await exec.runner.runDangerfileEnvironment( resolve(fixtures, "__DangerfileThrows.js"), undefined, runtime diff --git a/source/runner/runners/_tests/_transpiler.test.ts b/source/runner/runners/_tests/_transpiler.test.ts deleted file mode 100644 index 3727a144e..000000000 --- a/source/runner/runners/_tests/_transpiler.test.ts +++ /dev/null @@ -1,3 +0,0 @@ -it("doesn't just fail yet", () => { - expect(1).toBe(1) -}) diff --git a/source/runner/runners/utils/transpiler.ts b/source/runner/runners/utils/transpiler.ts index 160f5d3e1..4779831bf 100644 --- a/source/runner/runners/utils/transpiler.ts +++ b/source/runner/runners/utils/transpiler.ts @@ -31,14 +31,14 @@ try { // Now that we have a sense of what exists inside the users' node modules -const typescriptify = (content: string): string => { +export const typescriptify = (content: string): string => { const ts = require("typescript") // tslint:disable-line const compilerOptions = JSON.parse(fs.readFileSync("tsconfig.json", "utf8")) let result = ts.transpileModule(content, compilerOptions) return result.outputText } -const babelify = (content: string, filename: string, extraPlugins: string[]): string => { +export const babelify = (content: string, filename: string, extraPlugins: string[]): string => { const babel = require("babel-core") // tslint:disable-line if (!babel.transform) { return content @@ -52,6 +52,7 @@ const babelify = (content: string, filename: string, extraPlugins: string[]): st sourceMap: false, sourceFileName: null, sourceMapTarget: null, + sourceType: "module", plugins: [...extraPlugins, ...options.plugins], }