Add event log to editor

Shows output from git pull/push and npm install in the editor
This commit is contained in:
Nick O'Leary
2018-10-18 23:49:47 +01:00
parent 2816b3edae
commit b2516117f5
17 changed files with 535 additions and 224 deletions

View File

@@ -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);
},
/**

View File

@@ -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);
}
});
})
}
}

View File

@@ -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 },

View File

@@ -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,