diff --git a/Gruntfile.js b/Gruntfile.js
index 272290495..411b6305f 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -156,6 +156,7 @@ module.exports = function(grunt) {
"packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/editor.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/editors/*.js",
+ "packages/node_modules/@node-red/editor-client/src/js/ui/event-log.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/tray.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/clipboard.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/library.js",
diff --git a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json
index 304b9436d..9e0dcab33 100644
--- a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json
+++ b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json
@@ -215,6 +215,10 @@
"plusNMore": "+ __count__ more"
}
},
+ "eventLog": {
+ "title": "Event log",
+ "view": "View log"
+ },
"diff": {
"unresolvedCount": "__count__ unresolved conflict",
"unresolvedCount_plural": "__count__ unresolved conflicts",
diff --git a/packages/node_modules/@node-red/editor-client/src/js/keymap.json b/packages/node_modules/@node-red/editor-client/src/js/keymap.json
index 5b7a94f1f..b94290862 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/keymap.json
+++ b/packages/node_modules/@node-red/editor-client/src/js/keymap.json
@@ -19,7 +19,8 @@
"ctrl-alt-r": "core:show-remote-diff",
"ctrl-alt-n": "core:new-project",
"ctrl-alt-o": "core:open-project",
- "ctrl-g v": "core:show-version-control-tab"
+ "ctrl-g v": "core:show-version-control-tab",
+ "ctrl-shift-l": "core:show-event-log"
},
"workspace": {
"backspace": "core:delete-selection",
diff --git a/packages/node_modules/@node-red/editor-client/src/js/red.js b/packages/node_modules/@node-red/editor-client/src/js/red.js
index 4d8012cf4..7c5bd3dda 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/red.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/red.js
@@ -398,6 +398,10 @@ var RED = (function() {
// Refresh flow library to ensure any examples are updated
RED.library.loadFlowLibrary();
});
+ RED.comms.subscribe("event-log/#", function(topic,payload) {
+ var id = topic.substring(9);
+ RED.eventLog.log(id,payload);
+ });
}
function showAbout() {
@@ -436,6 +440,7 @@ var RED = (function() {
// null,
{id:"menu-item-palette",label:RED._("menu.label.palette.show"),toggle:true,onselect:"core:toggle-palette", selected: true},
{id:"menu-item-sidebar",label:RED._("menu.label.sidebar.show"),toggle:true,onselect:"core:toggle-sidebar", selected: true},
+ {id:"menu-item-event-log",label:RED._("eventLog.title"),onselect:"core:show-event-log"},
null
]});
menuOptions.push(null);
@@ -483,6 +488,7 @@ var RED = (function() {
RED.library.init();
RED.keyboard.init();
RED.palette.init();
+ RED.eventLog.init();
if (RED.settings.theme('palette.editable') !== false) {
RED.palette.editor.init();
} else {
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/event-log.js b/packages/node_modules/@node-red/editor-client/src/js/ui/event-log.js
new file mode 100644
index 000000000..c024b06fe
--- /dev/null
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/event-log.js
@@ -0,0 +1,121 @@
+/**
+ * Copyright JS Foundation and other contributors, http://js.foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+RED.eventLog = (function() {
+
+ var template = '';
+
+ var eventLogEditor;
+ var backlog = [];
+ var shown = false;
+
+ function appendLogLine(line) {
+ backlog.push(line);
+ if (backlog.length > 500) {
+ backlog = backlog.slice(-500);
+ }
+ if (eventLogEditor) {
+ eventLogEditor.getSession().insert({
+ row: eventLogEditor.getSession().getLength(),
+ column: 0
+ }, "\n" + line);
+ eventLogEditor.scrollToLine(eventLogEditor.getSession().getLength());
+ }
+ }
+ return {
+ init: function() {
+ $(template).appendTo(document.body);
+ RED.actions.add("core:show-event-log",RED.eventLog.show);
+ },
+ show: function() {
+ if (shown) {
+ return;
+ }
+ shown = true;
+ var type = "_eventLog"
+
+ var trayOptions = {
+ title: RED._("eventLog.title"),
+ width: Infinity,
+ buttons: [
+ {
+ id: "node-dialog-close",
+ text: RED._("common.label.close"),
+ click: function() {
+ RED.tray.close();
+ }
+ }
+ ],
+ resize: function(dimensions) {
+ var rows = $("#dialog-form>div:not(.node-text-editor-row)");
+ var editorRow = $("#dialog-form>div.node-text-editor-row");
+ var height = $("#dialog-form").height();
+ for (var i=0;i').appendTo(spinner);
+ $('').text(RED._("eventLog.view")).appendTo(buttonRow).click(function(evt) {
+ evt.preventDefault();
+ RED.actions.invoke("core:show-event-log");
+ });
+ RED.eventLog.startEvent(RED._("palette.editor.confirm.button.install")+" : "+entry.name+" "+version);
installNodeModule(entry.name,version,function(xhr) {
spinner.remove();
if (xhr) {
if (xhr.responseJSON) {
- RED.notify(RED._('palette.editor.errors.updateFailed',{module: entry.name,message:xhr.responseJSON.message}));
+ var notification = RED.notify(RED._('palette.editor.errors.updateFailed',{module: entry.name,message:xhr.responseJSON.message}),{
+ type: 'error',
+ modal: true,
+ fixed: true,
+ buttons: [
+ {
+ text: RED._("common.label.close"),
+ click: function() {
+ notification.close();
+ }
+ },{
+ text: RED._("eventLog.view"),
+ click: function() {
+ notification.close();
+ RED.actions.invoke("core:show-event-log");
+ }
+ }
+ ]
+ });
}
}
done(xhr);
@@ -898,12 +922,35 @@ RED.palette.editor = (function() {
class: "primary palette-module-install-confirm-button-remove",
click: function() {
var spinner = RED.utils.addSpinnerOverlay(container, true);
+ var buttonRow = $('').appendTo(spinner);
+ $('').text(RED._("eventLog.view")).appendTo(buttonRow).click(function(evt) {
+ evt.preventDefault();
+ RED.actions.invoke("core:show-event-log");
+ });
+ RED.eventLog.startEvent(RED._("palette.editor.confirm.button.remove")+" : "+entry.name);
removeNodeModule(entry.name, function(xhr) {
spinner.remove();
if (xhr) {
if (xhr.responseJSON) {
- RED.notify(RED._('palette.editor.errors.removeFailed',{module: entry.name,message:xhr.responseJSON.message}));
- }
+ var notification = RED.notify(RED._('palette.editor.errors.removeFailed',{module: entry.name,message:xhr.responseJSON.message}),{
+ type: 'error',
+ modal: true,
+ fixed: true,
+ buttons: [
+ {
+ text: RED._("common.label.close"),
+ click: function() {
+ notification.close();
+ }
+ },{
+ text: RED._("eventLog.view"),
+ click: function() {
+ notification.close();
+ RED.actions.invoke("core:show-event-log");
+ }
+ }
+ ]
+ }); }
}
})
notification.close();
@@ -940,11 +987,36 @@ RED.palette.editor = (function() {
class: "primary palette-module-install-confirm-button-install",
click: function() {
var spinner = RED.utils.addSpinnerOverlay(container, true);
+
+ var buttonRow = $('').appendTo(spinner);
+ $('').text(RED._("eventLog.view")).appendTo(buttonRow).click(function(evt) {
+ evt.preventDefault();
+ RED.actions.invoke("core:show-event-log");
+ });
+ RED.eventLog.startEvent(RED._("palette.editor.confirm.button.install")+" : "+entry.id+" "+entry.version);
installNodeModule(entry.id,entry.version,function(xhr) {
spinner.remove();
if (xhr) {
if (xhr.responseJSON) {
- RED.notify(RED._('palette.editor.errors.installFailed',{module: entry.id,message:xhr.responseJSON.message}));
+ var notification = RED.notify(RED._('palette.editor.errors.installFailed',{module: entry.id,message:xhr.responseJSON.message}),{
+ type: 'error',
+ modal: true,
+ fixed: true,
+ buttons: [
+ {
+ text: RED._("common.label.close"),
+ click: function() {
+ notification.close();
+ }
+ },{
+ text: RED._("eventLog.view"),
+ click: function() {
+ notification.close();
+ RED.actions.invoke("core:show-event-log");
+ }
+ }
+ ]
+ });
}
}
done(xhr);
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/projects/tab-versionControl.js b/packages/node_modules/@node-red/editor-client/src/js/ui/projects/tab-versionControl.js
index 22fbc12f3..a153fe4da 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/ui/projects/tab-versionControl.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/projects/tab-versionControl.js
@@ -870,7 +870,13 @@ RED.sidebar.versionControl = (function() {
.click(function(e) {
e.preventDefault();
var spinner = utils.addSpinnerOverlay(remoteBox).addClass("projects-dialog-spinner-contain");
+ var buttonRow = $('').appendTo(spinner);
+ $('').text(RED._("eventLog.view")).appendTo(buttonRow).click(function(evt) {
+ evt.preventDefault();
+ RED.actions.invoke("core:show-event-log");
+ });
var activeProject = RED.projects.getActiveProject();
+ RED.eventLog.startEvent("Push changes"+(activeProject.git.branches.remoteAlt?(" : "+activeProject.git.branches.remoteAlt):""));
var url = "projects/"+activeProject.name+"/push";
if (activeProject.git.branches.remoteAlt) {
url+="/"+activeProject.git.branches.remoteAlt;
@@ -914,7 +920,13 @@ RED.sidebar.versionControl = (function() {
var pullRemote = function(options) {
options = options || {};
var spinner = utils.addSpinnerOverlay(remoteBox).addClass("projects-dialog-spinner-contain");
+ var buttonRow = $('').appendTo(spinner);
+ $('').text(RED._("eventLog.view")).appendTo(buttonRow).click(function(evt) {
+ evt.preventDefault();
+ RED.actions.invoke("core:show-event-log");
+ });
var activeProject = RED.projects.getActiveProject();
+ RED.eventLog.startEvent("Pull changes"+(activeProject.git.branches.remoteAlt?(" : "+activeProject.git.branches.remoteAlt):""));
var url = "projects/"+activeProject.name+"/pull";
if (activeProject.git.branches.remoteAlt) {
url+="/"+activeProject.git.branches.remoteAlt;
diff --git a/packages/node_modules/@node-red/editor-client/src/sass/ace.scss b/packages/node_modules/@node-red/editor-client/src/sass/ace.scss
index 9425c356c..c5f85badc 100644
--- a/packages/node_modules/@node-red/editor-client/src/sass/ace.scss
+++ b/packages/node_modules/@node-red/editor-client/src/sass/ace.scss
@@ -6,3 +6,16 @@
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
+
+#event-log-editor {
+ .ace_scroller {
+ background: #444;
+ color: #dd9;
+ }
+ .ace_active-line {
+ background: #666 !important;
+ }
+ .ace_selection {
+ background: #999 !important;
+ }
+}
diff --git a/packages/node_modules/@node-red/registry/lib/installer.js b/packages/node_modules/@node-red/registry/lib/installer.js
index 9ec84a2f3..66768a620 100644
--- a/packages/node_modules/@node-red/registry/lib/installer.js
+++ b/packages/node_modules/@node-red/registry/lib/installer.js
@@ -21,6 +21,7 @@ var fs = require("fs");
var registry = require("./registry");
var library = require("./library");
var log;
+var exec;
var events;
@@ -36,6 +37,7 @@ function init(runtime) {
events = runtime.events;
settings = runtime.settings;
log = runtime.log;
+ exec = runtime.exec;
}
var activePromise = Promise.resolve();
@@ -104,50 +106,40 @@ function installModule(module,version) {
var installDir = settings.userDir || process.env.NODE_RED_HOME || ".";
var args = ['install','--save','--save-prefix="~"','--production',installName];
log.trace(npmCommand + JSON.stringify(args));
- var child = child_process.spawn(npmCommand,args,{
- cwd: installDir,
- shell: true
- });
- var output = "";
- child.stdout.on('data', (data) => {
- output += data;
- });
- child.stderr.on('data', (data) => {
- output += data;
- });
- child.on('close', (code) => {
- if (code !== 0) {
- var e;
- var lookFor404 = new RegExp(" 404 .*"+module,"m");
- var lookForVersionNotFound = new RegExp("version not found: "+module+"@"+version,"m");
- if (lookFor404.test(output)) {
- log.warn(log._("server.install.install-failed-not-found",{name:module}));
- e = new Error("Module not found");
- e.code = 404;
- reject(e);
- } else if (isUpgrade && lookForVersionNotFound.test(output)) {
- log.warn(log._("server.install.upgrade-failed-not-found",{name:module}));
- e = new Error("Module not found");
- e.code = 404;
- reject(e);
- } else {
- log.warn(log._("server.install.install-failed-long",{name:module}));
- log.warn("------------------------------------------");
- log.warn(output);
- log.warn("------------------------------------------");
- reject(new Error(log._("server.install.install-failed")));
- }
+ exec.run(npmCommand,args,{
+ cwd: installDir
+ }, true).then(result => {
+ if (!isUpgrade) {
+ log.info(log._("server.install.installed",{name:module}));
+ resolve(require("./index").addModule(module).then(reportAddedModules));
} else {
- if (!isUpgrade) {
- log.info(log._("server.install.installed",{name:module}));
- resolve(require("./index").addModule(module).then(reportAddedModules));
- } else {
- log.info(log._("server.install.upgraded",{name:module, version:version}));
- events.emit("runtime-event",{id:"restart-required",payload:{type:"warning",text:"notification.warnings.restartRequired"},retain:true});
- resolve(require("./registry").setModulePendingUpdated(module,version));
- }
+ log.info(log._("server.install.upgraded",{name:module, version:version}));
+ events.emit("runtime-event",{id:"restart-required",payload:{type:"warning",text:"notification.warnings.restartRequired"},retain:true});
+ resolve(require("./registry").setModulePendingUpdated(module,version));
}
- });
+ }).catch(result => {
+ var output = result.stderr;
+ var e;
+ var lookFor404 = new RegExp(" 404 .*"+module,"m");
+ var lookForVersionNotFound = new RegExp("version not found: "+module+"@"+version,"m");
+ if (lookFor404.test(output)) {
+ log.warn(log._("server.install.install-failed-not-found",{name:module}));
+ e = new Error("Module not found");
+ e.code = 404;
+ reject(e);
+ } else if (isUpgrade && lookForVersionNotFound.test(output)) {
+ log.warn(log._("server.install.upgrade-failed-not-found",{name:module}));
+ e = new Error("Module not found");
+ e.code = 404;
+ reject(e);
+ } else {
+ log.warn(log._("server.install.install-failed-long",{name:module}));
+ log.warn("------------------------------------------");
+ log.warn(output);
+ log.warn("------------------------------------------");
+ reject(new Error(log._("server.install.install-failed")));
+ }
+ })
});
}).catch(err => {
// In case of error, reset activePromise to be resolvable
@@ -208,25 +200,21 @@ function uninstallModule(module) {
var args = ['remove','--save',module];
log.trace(npmCommand + JSON.stringify(args));
- var child = child_process.execFile(npmCommand,args,
- {
- cwd: installDir
- },
- function(err, stdin, stdout) {
- if (err) {
- log.warn(log._("server.install.uninstall-failed-long",{name:module}));
- log.warn("------------------------------------------");
- log.warn(err.toString());
- log.warn("------------------------------------------");
- reject(new Error(log._("server.install.uninstall-failed",{name:module})));
- } else {
- log.info(log._("server.install.uninstalled",{name:module}));
- reportRemovedModules(list);
- library.removeExamplesDir(module);
- resolve(list);
- }
- }
- );
+ exec.run(npmCommand,args,{
+ cwd: installDir,
+ },true).then(result => {
+ log.info(log._("server.install.uninstalled",{name:module}));
+ reportRemovedModules(list);
+ library.removeExamplesDir(module);
+ resolve(list);
+ }).catch(result => {
+ var output = result.stderr;
+ log.warn(log._("server.install.uninstall-failed-long",{name:module}));
+ log.warn("------------------------------------------");
+ log.warn(output.toString());
+ log.warn("------------------------------------------");
+ reject(new Error(log._("server.install.uninstall-failed",{name:module})));
+ });
});
}).catch(err => {
// In case of error, reset activePromise to be resolvable
diff --git a/packages/node_modules/@node-red/runtime/lib/api/comms.js b/packages/node_modules/@node-red/runtime/lib/api/comms.js
index 3ab361f3d..7713fd3dc 100644
--- a/packages/node_modules/@node-red/runtime/lib/api/comms.js
+++ b/packages/node_modules/@node-red/runtime/lib/api/comms.js
@@ -42,6 +42,21 @@ function handleRuntimeEvent(event) {
runtime.log.trace("runtime event: "+JSON.stringify(event));
publish("notification/"+event.id,event.payload||{},event.retain);
}
+function handleEventLog(event) {
+ var type = event.payload.type;
+ var id = event.id;
+ if (event.payload.data) {
+ var data = event.payload.data;
+ if (data.endsWith('\n')) {
+ data = data.substring(0,data.length-1);
+ }
+ var lines = data.split(/\n/);
+ lines.forEach(line => {
+ runtime.log.debug((type?("["+type+"] "):"")+line)
+ })
+ }
+ publish("event-log/"+event.id,event.payload||{});
+}
function publish(topic,data,retain) {
if (retain) {
@@ -64,6 +79,8 @@ var api = module.exports = {
runtime.events.on("runtime-event",handleRuntimeEvent);
runtime.events.removeListener("comms",handleCommsEvent);
runtime.events.on("comms",handleCommsEvent);
+ runtime.events.removeListener("event-log",handleEventLog);
+ runtime.events.on("event-log",handleEventLog);
},
/**
diff --git a/packages/node_modules/@node-red/runtime/lib/exec.js b/packages/node_modules/@node-red/runtime/lib/exec.js
new file mode 100644
index 000000000..0ef3c069c
--- /dev/null
+++ b/packages/node_modules/@node-red/runtime/lib/exec.js
@@ -0,0 +1,69 @@
+/**
+ * Copyright JS Foundation and other contributors, http://js.foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+
+const child_process = require('child_process');
+const { util } = require('@node-red/util');
+
+var events;
+
+function logLines(id,type,data) {
+ events.emit("event-log", {id:id,payload:{ts: Date.now(),data:data,type:type}});
+}
+
+module.exports = {
+ init: function(_runtime) {
+ events = _runtime.events;
+ },
+ run: function(command,args,options,emit) {
+ var invocationId = util.generateId();
+
+ emit && events.emit("event-log", {ts: Date.now(),id:invocationId,payload:{ts: Date.now(),data:command+" "+args.join(" ")}});
+
+ return new Promise((resolve, reject) => {
+ let stdout = "";
+ let stderr = "";
+ const child = child_process.spawn(command,args,options);
+ child.stdout.on('data', (data) => {
+ const str = ""+data;
+ stdout += str;
+ emit && logLines(invocationId,"out",str);
+ });
+ child.stderr.on('data', (data) => {
+ const str = ""+data;
+ stderr += str;
+ emit && logLines(invocationId,"err",str);
+ });
+ child.on('error', function(err) {
+ stderr = err.toString();
+ emit && logLines(invocationId,"err",stderr);
+ })
+ child.on('close', (code) => {
+ let result = {
+ code: code,
+ stdout: stdout,
+ stderr: stderr
+ }
+ emit && events.emit("event-log", {id:invocationId,payload:{ts: Date.now(),data:"rc="+code}});
+
+ if (code === 0) {
+ resolve(result)
+ } else {
+ reject(result);
+ }
+ });
+ })
+ }
+}
diff --git a/packages/node_modules/@node-red/runtime/lib/index.js b/packages/node_modules/@node-red/runtime/lib/index.js
index fb4e74b93..fd6e59794 100644
--- a/packages/node_modules/@node-red/runtime/lib/index.js
+++ b/packages/node_modules/@node-red/runtime/lib/index.js
@@ -23,6 +23,7 @@ var storage = require("./storage");
var library = require("./library");
var events = require("./events");
var settings = require("./settings");
+var exec = require("./exec");
var express = require("express");
var path = require('path');
@@ -71,6 +72,7 @@ function init(userSettings,_redUtil,_adminApi) {
redNodes.init(runtime);
library.init(runtime);
externalAPI.init(runtime);
+ exec.init(runtime);
}
var version;
@@ -248,6 +250,7 @@ var runtime = {
events: events,
nodes: redNodes,
library: library,
+ exec: exec,
util: require("@node-red/util").util,
get adminApi() { return adminApi },
get nodeApp() { return nodeApp },
diff --git a/packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/git/index.js b/packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/git/index.js
index b6f3fa61a..d5c23ccb6 100644
--- a/packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/git/index.js
+++ b/packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/git/index.js
@@ -15,8 +15,9 @@
**/
var when = require('when');
-var exec = require('child_process').exec;
-var spawn = require('child_process').spawn;
+
+var exec = require("../../../../exec");
+
var authResponseServer = require('./authServer').ResponseServer;
var sshResponseServer = require('./authServer').ResponseSSHServer;
var clone = require('clone');
@@ -26,83 +27,70 @@ var gitCommand = "git";
var gitVersion;
var log;
-function runGitCommand(args,cwd,env) {
+function runGitCommand(args,cwd,env,emit) {
log.trace(gitCommand + JSON.stringify(args));
- return when.promise(function(resolve,reject) {
- args.unshift("credential.helper=")
- args.unshift("-c");
- var child = spawn(gitCommand, args, {cwd:cwd, detached:true, env:env});
- var stdout = "";
- var stderr = "";
- child.stdout.on('data', function(data) {
- stdout += data;
- });
- child.stderr.on('data', function(data) {
- stderr += data;
- });
- child.on('error', function(err) {
- stderr = err.toString();
- })
- child.on('close', function(code) {
- if (code !== 0) {
- var err = new Error(stderr);
- err.stdout = stdout;
- err.stderr = stderr;
- if (/Connection refused/i.test(stderr)) {
- err.code = "git_connection_failed";
- } else if (/Connection timed out/i.test(stderr)) {
- err.code = "git_connection_failed";
- } else if (/fatal: could not read/i.test(stderr)) {
- // Username/Password
- err.code = "git_auth_failed";
- } else if(/HTTP Basic: Access denied/i.test(stderr)) {
- err.code = "git_auth_failed";
- } else if(/Permission denied \(publickey\)/i.test(stderr)) {
- err.code = "git_auth_failed";
- } else if(/Host key verification failed/i.test(stderr)) {
- // TODO: handle host key verification errors separately
- err.code = "git_auth_failed";
- } else if (/commit your changes or stash/i.test(stderr)) {
- err.code = "git_local_overwrite";
- } else if (/CONFLICT/.test(err.stdout)) {
- err.code = "git_pull_merge_conflict";
- } else if (/not fully merged/i.test(stderr)) {
- err.code = "git_delete_branch_unmerged";
- } else if (/remote .* already exists/i.test(stderr)) {
- err.code = "git_remote_already_exists";
- } else if (/does not appear to be a git repository/i.test(stderr)) {
- err.code = "git_not_a_repository";
- } else if (/Repository not found/i.test(stderr)) {
- err.code = "git_repository_not_found";
- } else if (/repository '.*' does not exist/i.test(stderr)) {
- err.code = "git_repository_not_found";
- } else if (/refusing to merge unrelated histories/i.test(stderr)) {
- err.code = "git_pull_unrelated_history"
- } else if (/Please tell me who you are/i.test(stderr)) {
- err.code = "git_missing_user";
- } else if (/name consists only of disallowed characters/i.test(stderr)) {
- err.code = "git_missing_user";
- }
- return reject(err);
- }
- resolve(stdout);
- });
- });
+ args.unshift("credential.helper=")
+ args.unshift("-c");
+ return exec.run(gitCommand, args, {cwd:cwd, env:env}, emit).then(result => {
+ return result.stdout;
+ }).catch(result => {
+ var err = new Error(stderr);
+ var stdout = result.stdout;
+ var stderr = result.stderr;
+ err.stdout = stdout;
+ err.stderr = stderr;
+ if (/Connection refused/i.test(stderr)) {
+ err.code = "git_connection_failed";
+ } else if (/Connection timed out/i.test(stderr)) {
+ err.code = "git_connection_failed";
+ } else if (/fatal: could not read/i.test(stderr)) {
+ // Username/Password
+ err.code = "git_auth_failed";
+ } else if(/HTTP Basic: Access denied/i.test(stderr)) {
+ err.code = "git_auth_failed";
+ } else if(/Permission denied \(publickey\)/i.test(stderr)) {
+ err.code = "git_auth_failed";
+ } else if(/Host key verification failed/i.test(stderr)) {
+ // TODO: handle host key verification errors separately
+ err.code = "git_auth_failed";
+ } else if (/commit your changes or stash/i.test(stderr)) {
+ err.code = "git_local_overwrite";
+ } else if (/CONFLICT/.test(err.stdout)) {
+ err.code = "git_pull_merge_conflict";
+ } else if (/not fully merged/i.test(stderr)) {
+ err.code = "git_delete_branch_unmerged";
+ } else if (/remote .* already exists/i.test(stderr)) {
+ err.code = "git_remote_already_exists";
+ } else if (/does not appear to be a git repository/i.test(stderr)) {
+ err.code = "git_not_a_repository";
+ } else if (/Repository not found/i.test(stderr)) {
+ err.code = "git_repository_not_found";
+ } else if (/repository '.*' does not exist/i.test(stderr)) {
+ err.code = "git_repository_not_found";
+ } else if (/refusing to merge unrelated histories/i.test(stderr)) {
+ err.code = "git_pull_unrelated_history"
+ } else if (/Please tell me who you are/i.test(stderr)) {
+ err.code = "git_missing_user";
+ } else if (/name consists only of disallowed characters/i.test(stderr)) {
+ err.code = "git_missing_user";
+ }
+ throw err;
+ })
}
-function runGitCommandWithAuth(args,cwd,auth) {
+function runGitCommandWithAuth(args,cwd,auth,emit) {
return authResponseServer(auth).then(function(rs) {
var commandEnv = clone(process.env);
commandEnv.GIT_ASKPASS = path.join(__dirname,"node-red-ask-pass.sh");
commandEnv.NODE_RED_GIT_NODE_PATH = process.execPath;
commandEnv.NODE_RED_GIT_SOCK_PATH = rs.path;
commandEnv.NODE_RED_GIT_ASKPASS_PATH = path.join(__dirname,"authWriter.js");
- return runGitCommand(args,cwd,commandEnv).finally(function() {
+ return runGitCommand(args,cwd,commandEnv,emit).finally(function() {
rs.close();
});
})
}
-function runGitCommandWithSSHCommand(args,cwd,auth) {
+function runGitCommandWithSSHCommand(args,cwd,auth,emit) {
return sshResponseServer(auth).then(function(rs) {
var commandEnv = clone(process.env);
commandEnv.SSH_ASKPASS = path.join(__dirname,"node-red-ask-pass.sh");
@@ -116,7 +104,7 @@ function runGitCommandWithSSHCommand(args,cwd,auth) {
// GIT_SSH_COMMAND - added in git 2.3.0
commandEnv.GIT_SSH_COMMAND = "ssh -i " + auth.key_path + " -F /dev/null";
// console.log('commandEnv:', commandEnv);
- return runGitCommand(args,cwd,commandEnv).finally(function() {
+ return runGitCommand(args,cwd,commandEnv,emit).finally(function() {
rs.close();
});
})
@@ -448,13 +436,13 @@ module.exports = {
var promise;
if (auth) {
if ( auth.key_path ) {
- promise = runGitCommandWithSSHCommand(args,cwd,auth);
+ promise = runGitCommandWithSSHCommand(args,cwd,auth,true);
}
else {
- promise = runGitCommandWithAuth(args,cwd,auth);
+ promise = runGitCommandWithAuth(args,cwd,auth,true);
}
} else {
- promise = runGitCommand(args,cwd)
+ promise = runGitCommand(args,cwd,undefined,true)
}
return promise;
// .catch(function(err) {
@@ -485,13 +473,13 @@ module.exports = {
var promise;
if (auth) {
if ( auth.key_path ) {
- promise = runGitCommandWithSSHCommand(args,cwd,auth);
+ promise = runGitCommandWithSSHCommand(args,cwd,auth,true);
}
else {
- promise = runGitCommandWithAuth(args,cwd,auth);
+ promise = runGitCommandWithAuth(args,cwd,auth,true);
}
} else {
- promise = runGitCommand(args,cwd)
+ promise = runGitCommand(args,cwd,undefined,true)
}
return promise.catch(function(err) {
if (err.code === 'git_error') {
@@ -517,13 +505,13 @@ module.exports = {
args.push(".");
if (auth) {
if ( auth.key_path ) {
- return runGitCommandWithSSHCommand(args,cwd,auth);
+ return runGitCommandWithSSHCommand(args,cwd,auth,true);
}
else {
- return runGitCommandWithAuth(args,cwd,auth);
+ return runGitCommandWithAuth(args,cwd,auth,true);
}
} else {
- return runGitCommand(args,cwd);
+ return runGitCommand(args,cwd,undefined,true);
}
},
getStatus: getStatus,
diff --git a/packages/node_modules/@node-red/util/index.js b/packages/node_modules/@node-red/util/index.js
index 7fa63fc7d..cc7f502f8 100644
--- a/packages/node_modules/@node-red/util/index.js
+++ b/packages/node_modules/@node-red/util/index.js
@@ -30,19 +30,22 @@ module.exports = {
log.init(settings);
i18n.init();
},
+
/**
* Logging utilities
* @see module:@node-red/util.module:log
*/
log: log,
+
/**
* Internationalization utilities
* @see module:@node-red/util.module:i18n
*/
i18n: i18n,
+
/**
* General utilities
* @see module:@node-red/util.module:util
*/
- util: util
+ util: util,
}
diff --git a/packages/node_modules/node-red/lib/red.js b/packages/node_modules/node-red/lib/red.js
index b6eed50ac..a25b30fd3 100644
--- a/packages/node_modules/node-red/lib/red.js
+++ b/packages/node_modules/node-red/lib/red.js
@@ -38,18 +38,6 @@ function checkVersion(userSettings) {
}
}
-function checkBuild() {
- console.log("Skipping red.js/checkBuild");
- // var editorFile = path.resolve(path.join(__dirname,"..","..","..","..","public","red","red.min.js"));
- // try {
- // var stats = fs.statSync(editorFile);
- // } catch(err) {
- // var e = new Error("Node-RED not built");
- // e.code = "not_built";
- // throw e;
- // }
-}
-
module.exports = {
init: function(httpServer,userSettings) {
if (!userSettings) {
@@ -59,7 +47,6 @@ module.exports = {
if (!userSettings.SKIP_BUILD_CHECK) {
checkVersion(userSettings);
- checkBuild();
}
if (!userSettings.coreNodesDir) {
diff --git a/test/unit/@node-red/registry/lib/installer_spec.js b/test/unit/@node-red/registry/lib/installer_spec.js
index 65747d185..f3bae8b3a 100644
--- a/test/unit/@node-red/registry/lib/installer_spec.js
+++ b/test/unit/@node-red/registry/lib/installer_spec.js
@@ -21,9 +21,6 @@ var path = require("path");
var fs = require('fs');
var EventEmitter = require('events');
-var child_process = require('child_process');
-
-
var NR_TEST_UTILS = require("nr-test-utils");
var installer = NR_TEST_UTILS.require("@node-red/registry/lib/installer");
@@ -42,16 +39,21 @@ describe('nodes/registry/installer', function() {
_: function() { return "abc"}
}
- before(function() {
- installer.init({log:mockLog, settings:{}, events: new EventEmitter()});
+ beforeEach(function() {
+ installer.init({log:mockLog, settings:{}, events: new EventEmitter(), exec: {
+ run: function() {
+ return Promise.resolve("");
+ }
+ }});
});
+ function initInstaller(execResult) {
+ installer.init({log:mockLog, settings:{}, events: new EventEmitter(), exec: {
+ run: function() {
+ return execResult;
+ }
+ }});
+ }
afterEach(function() {
- if (child_process.spawn.restore) {
- child_process.spawn.restore();
- }
- if (child_process.execFile.restore) {
- child_process.execFile.restore();
- }
if (registry.addModule.restore) {
registry.addModule.restore();
}
@@ -76,39 +78,33 @@ describe('nodes/registry/installer', function() {
describe("installs module", function() {
it("rejects when npm returns a 404", function(done) {
- sinon.stub(child_process,"spawn",function(cmd,args,opt) {
- var ee = new EventEmitter();
- ee.stdout = new EventEmitter();
- ee.stderr = new EventEmitter();
- setTimeout(function() {
- ee.stderr.emit('data'," 404 this_wont_exist");
- ee.emit('close',1);
- },10)
- return ee;
- });
-
+ var res = {
+ code: 1,
+ stdout:"",
+ stderr:" 404 this_wont_exist"
+ }
+ var p = Promise.reject(res);
+ p.catch((err)=>{});
+ initInstaller(p)
installer.installModule("this_wont_exist").catch(function(err) {
err.should.have.property("code",404);
done();
});
});
it("rejects when npm does not find specified version", function(done) {
- sinon.stub(child_process,"spawn",function(cmd,args,opt) {
- var ee = new EventEmitter();
- ee.stdout = new EventEmitter();
- ee.stderr = new EventEmitter();
- setTimeout(function() {
- ee.stderr.emit('data'," version not found: this_wont_exist@0.1.2");
- ee.emit('close',1);
- },10)
- return ee;
- });
+ var res = {
+ code: 1,
+ stdout:"",
+ stderr:" version not found: this_wont_exist@0.1.2"
+ }
+ var p = Promise.reject(res);
+ p.catch((err)=>{});
+ initInstaller(p)
sinon.stub(typeRegistry,"getModuleInfo", function() {
return {
version: "0.1.1"
}
});
-
installer.installModule("this_wont_exist","0.1.2").catch(function(err) {
err.code.should.be.eql(404);
done();
@@ -126,17 +122,14 @@ describe('nodes/registry/installer', function() {
});
});
it("rejects with generic error", function(done) {
- sinon.stub(child_process,"spawn",function(cmd,args,opt,cb) {
- var ee = new EventEmitter();
- ee.stdout = new EventEmitter();
- ee.stderr = new EventEmitter();
- setTimeout(function() {
- ee.stderr.emit('data'," kaboom!");
- ee.emit('close',1);
- },10)
- return ee;
- });
-
+ var res = {
+ code: 1,
+ stdout:"",
+ stderr:" kaboom!"
+ }
+ var p = Promise.reject(res);
+ p.catch((err)=>{});
+ initInstaller(p)
installer.installModule("this_wont_exist").then(function() {
done(new Error("Unexpected success"));
}).catch(function(err) {
@@ -145,15 +138,16 @@ describe('nodes/registry/installer', function() {
});
it("succeeds when module is found", function(done) {
var nodeInfo = {nodes:{module:"foo",types:["a"]}};
- sinon.stub(child_process,"spawn",function(cmd,args,opt) {
- var ee = new EventEmitter();
- ee.stdout = new EventEmitter();
- ee.stderr = new EventEmitter();
- setTimeout(function() {
- ee.emit('close',0);
- },10)
- return ee;
- });
+
+ var res = {
+ code: 0,
+ stdout:"",
+ stderr:""
+ }
+ var p = Promise.resolve(res);
+ p.catch((err)=>{});
+ initInstaller(p)
+
var addModule = sinon.stub(registry,"addModule",function(md) {
return when.resolve(nodeInfo);
});
@@ -191,15 +185,15 @@ describe('nodes/registry/installer', function() {
return when.resolve(nodeInfo);
});
var resourcesDir = path.resolve(path.join(__dirname,"resources","local","TestNodeModule","node_modules","TestNodeModule"));
- sinon.stub(child_process,"spawn",function(cmd,args,opt) {
- var ee = new EventEmitter();
- ee.stdout = new EventEmitter();
- ee.stderr = new EventEmitter();
- setTimeout(function() {
- ee.emit('close',0);
- },10)
- return ee;
- });
+
+ var res = {
+ code: 0,
+ stdout:"",
+ stderr:""
+ }
+ var p = Promise.resolve(res);
+ p.catch((err)=>{});
+ initInstaller(p)
installer.installModule(resourcesDir).then(function(info) {
info.should.eql(nodeInfo);
done();
@@ -226,9 +220,14 @@ describe('nodes/registry/installer', function() {
var removeModule = sinon.stub(registry,"removeModule",function(md) {
return when.resolve(nodeInfo);
});
- sinon.stub(child_process,"execFile",function(cmd,args,opt,cb) {
- cb(new Error("test_error"),"","");
- });
+ var res = {
+ code: 1,
+ stdout:"",
+ stderr:"error"
+ }
+ var p = Promise.reject(res);
+ p.catch((err)=>{});
+ initInstaller(p)
installer.uninstallModule("this_wont_exist").then(function() {
done(new Error("Unexpected success"));
@@ -244,9 +243,14 @@ describe('nodes/registry/installer', function() {
var getModuleInfo = sinon.stub(registry,"getModuleInfo",function(md) {
return {nodes:[]};
});
- sinon.stub(child_process,"execFile",function(cmd,args,opt,cb) {
- cb(null,"","");
- });
+ var res = {
+ code: 0,
+ stdout:"",
+ stderr:""
+ }
+ var p = Promise.resolve(res);
+ p.catch((err)=>{});
+ initInstaller(p)
sinon.stub(fs,"statSync", function(fn) { return {}; });
diff --git a/test/unit/@node-red/runtime/lib/exec_spec.js b/test/unit/@node-red/runtime/lib/exec_spec.js
new file mode 100644
index 000000000..e0369e07c
--- /dev/null
+++ b/test/unit/@node-red/runtime/lib/exec_spec.js
@@ -0,0 +1,22 @@
+/**
+ * Copyright JS Foundation and other contributors, http://js.foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+var should = require("should");
+var sinon = require("sinon");
+var path = require("path");
+
+var NR_TEST_UTILS = require("nr-test-utils");
+
+var exec = NR_TEST_UTILS.require("@node-red/runtime/lib/exec");