How can I get msysgit on windows to invoke node.js scripts in hooks? - node.js

I would like to use node.js scripts as my git hook scripts. Currently I'm experimenting on my Windows machine with Bonobo Git HTTP service which is attached to msysgit Git-1.9.0-preview20140217 portable installation. It works fine, tested with copuple pulls and pushes.
I have nodejs up and running but my git update script just fails without any clear reason. It definitely works - it started to always reject pushes as soon as I added it into the hooks folder on the server but there are no any clues in SourceTree output log.
Here is my update script:
#!C:/nodejs/node.exe
console.log('Hello world!');
console.log('Hello world 2!');
I tried back slashes, I tried double forward slashes, I tried quoting the path - no results.
Server response is as follows:
git -c diff.mnemonicprefix=false -c core.quotepath=false push -v --tags origin master:master
Pushing to http://myurl.git
POST git-receive-pack (4686 bytes)
remote: error: hook declined to update refs/heads/master[K
To http://myurl.git
! [remote rejected] master -> master (hook declined)
error: failed to push some refs to 'http://myurl.git'
Completed with errors, see above.
P.S. I don't know where [K comes from, my gusess is that it's corrupted newline character.

I ended up using similar approach to the one I found someone was using for Python.
I created a C# console app as follows:
using System;
using System.Diagnostics;
using System.IO;
namespace GitNodeRunner
{
class Program
{
static int Main(string[] args)
{
if (args == null || args.Length == 0)
{
Console.Error.WriteLine("Path to script file not specified");
return -1;
}
string argString = string.Join(" ", args);
Console.WriteLine("Node.js arguments received: {0}", argString);
string nodePath = Environment.GetEnvironmentVariable("NODE_HOME",
EnvironmentVariableTarget.Machine); // without "Machine" Git's Bash loses it
if (string.IsNullOrWhiteSpace(nodePath))
{
Console.Error.WriteLine("NODE_HOME global envirnoment variable not found");
return -1;
}
nodePath = Path.Combine(nodePath, "node.exe");
ProcessStartInfo start = new ProcessStartInfo
{
UseShellExecute = false,
Arguments = argString,
FileName = nodePath,
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
};
Process process = new Process();
process.StartInfo = start;
process.EnableRaisingEvents = true;
process.OutputDataReceived += process_OutputDataReceived;
process.ErrorDataReceived += process_OutputDataReceived;
process.Start();
process.BeginErrorReadLine();
process.BeginOutputReadLine();
process.WaitForExit();
return process.ExitCode;
}
static void process_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
string s = e.Data;
Console.Out.WriteLine(s);
}
static void process_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
string s = e.Data;
Console.Error.WriteLine(s);
}
}
}
I compiled it as git-nodejs.exe, put into Git's bin folder, ensured that I have NODE_HOME environment variable defined, and now I can use node.js scripts. As an example, I provide my node.js update hook which rejects pushes with malformed comment messages:
#!/bin/git-nodejs
// bin/git-nodejs is our custom C# programmed node launcher
// the following code is ported from http://git-scm.com/book/en/Customizing-Git-An-Example-Git-Enforced-Policy
// we have some offset from standard Git args here:
// 0 is path to node
// 1 is path to this script
var exec = require('child_process').exec,
refname = process.argv[2],
oldrev = process.argv[3],
newrev = process.argv[4],
regexMatch = /^REF #\d* /; // in my case I require strict start with "REF #number "
console.log("Enforcing policies in branch " + refname + " for commits between revisions " + oldrev + " and " + newrev);
// we have a special case - if this is a new branch, oldrev..newrev will cause git to throw an error,
// that's why we check for empty branch commit ID
var revListCmd = (oldrev === "0000000000000000000000000000000000000000") ?
"git rev-list " + newrev + " --not --branches=*" :
"git rev-list " + oldrev + ".." + newrev;
exec(revListCmd,
function (error, stdout, stderr) {
if (error !== null) {
console.error("Exec error: " + error);
exitError();
}
var missedRevs = stdout.split("\n");
missedRevs.forEach(function(rev){
// there is a redundant empty entry at the end after split()
if (rev.length === 0) {
return;
}
console.log("Verifying policy for revision " + rev);
// | sed '1,/^$/d' does not quite work as expected on Windows
// - it returns all commit details instead of comment
// thus we complete the processing from JavaScript
exec("git cat-file commit " + rev,
function (error, stdout, stderr) {
if (error !== null) {
console.error("Exec error: " + error);
exitError();
}
// we skip the blank line (two sequential newlines) and take everything that follows
// not sure if this is the best solution but "it works on my machine"
var commitMessage = stdout.split("\n\n")[1];
// notice: if commit message missing, git will report it as "no message"
if (commitMessage.match(regexMatch) === null) {
console.error("Commit message '" + commitMessage + "' does not match the required pattern " + regexMatch);
exitError();
}
});
});
});
//////////////////////////
// some helpers
function exitError()
{
// hack for bug https://github.com/joyent/node/issues/3584
// delay exit until stdout on Windows has had some time to flush
setTimeout(function() {
console.error("Exiting with error status 1");
process.exit(1);
}, 1000);
}

Related

VBS to SFTP WinSCP

I am trying to put log files into a SFTP Server. When I try to run I get error Line 1 Char 28 Syntax error. Anyone have any Idea to different code they got working for VBS? Looking for something simple.
cscript Transfer.vbs /type:winscp /SourceFolder:PATH TO LOG DIR /FTPType:sftp /FTPSite: SFTPSITE:PORT /FTPUser:USER /FTPPass:PASS
<job>
<reference object="WinSCP.Session" />
<script language="JScript">
try
{
// Setup session options
var sessionOptions = WScript.CreateObject("WinSCP.SessionOptions");
sessionOptions.Protocol = Protocol_Sftp;
sessionOptions.HostName = "SFTP";
sessionOptions.UserName = "USER";
sessionOptions.Password = "PASS";
var session = WScript.CreateObject("WinSCP.Session");
try
{
// Connect
session.Open(sessionOptions);
// Upload files
var transferOptions = WScript.CreateObject("WinSCP.TransferOptions");
transferOptions.TransferMode = TransferMode_Binary;
var transferResult = session.PutFiles("c:\\Users\PATH TO LOGS\\*", "/", false, transferOptions);
// Throw on any error
transferResult.Check();
// Print results
for (var enumerator = new Enumerator(transferResult.Transfers); !enumerator.atEnd(); enumerator.moveNext())
{
WScript.Echo("Upload of " + enumerator.item().FileName + " succeeded");
}
}
finally
{
// Disconnect, clean up
session.Dispose();
}
}
catch (e)
{
WScript.Echo("Error: " + e.message);
WScript.Quit(1);
}
</script>
</job>`
So I just had to install the SDK for Windows machine. Register the .dll file that winscp gives you. Also register via Com as well. Thakn you for looking into it

How to disable .save command in Node.js repl?

I'm a newbee to Node.js. I was reading REPL api just now, I assumed setting environment variable NODE_REPL_HISTORY to "" would turn off .save command which produces command lines history file. Was I wrong?
So I decided to set it by process module:
var repl = require("repl");
process.env['NODE_REPL_HISTORY'] = "";
var replServer = repl.start({
prompt:"my-app > ",
});
console.log(process.env);
var add = function(a,b){
return a+b;
};
replServer.context.foo = "bar";
replServer.context.add = add;
Unluckily, REPL still produced command lines history file.
.save command is defined in lib/repl.js unconditionally, that is it's present regardless of any environment variables.
repl.defineCommand('save', {
help: 'Save all evaluated commands in this REPL session to a file',
action: function(file) {
try {
fs.writeFileSync(file, this.lines.join('\n') + '\n');
this.outputStream.write('Session saved to:' + file + '\n');
} catch (e) {
this.outputStream.write('Failed to save:' + file + '\n');
}
this.displayPrompt();
}
});
You can delete this command by removing it manually from replServer:
delete replServer.commands.save;

Brunch plugin pipline issues

I'm writing a plugin for Brunch to 'filter' files from code library. Basic idea is to:
check my source files (in src\ folder, or any watched folders that don't match library pattern),
build a list of imported/required modules from code library (in lib\ folder, outside src\, somewhere on disk)
check files against this list and 'approve' or 'reject' them
compile only what's 'approved', so I don't end up with huge files that have all modules/components from my library, but only what I use in particular project
When I work only with JavaScript files this.pattern = /.*(js|jsx)$/; everything works fine. Next step is to include more files, since many modules/components in library have some sort of template or stylesheets, for example this is one AngularJS module:
lib\
modules\
pager\
controller.jsx
directive.jsx
template.html
pager.styl
README.md
But when I expand the pattern to include other files this.pattern = /.*/;, I run into all sorts of issues (; Most have to do with pipline - those are the kinds of errors I'm getting. For example:
jshint-brunch doesn't like README.md
html-brunch won't wrap template.html
stylus-brunch and sass-brunch are also unhappy
I've tried solving these problems individually, for example if I disable html-brunch config.plugins.off: ['html-brunch'], and add this code inside the compiler function, it kinda works:
if( params.path.match(/.html$/) ) {
params.data = "module.exports = function() { return " + JSON.stringify(params.data) + ";};";
return callback(null, this.config.modules.wrapper(params.path, params.data));
}
..but I couldn't resolve all the issues. Pretty much all problems have to do with this line in the compiler function: return callback(null, null);. When I 'reject' a file next plugin gets something undefined and breaks...
Any ideas how to solve this?
I'd like to eventually expand plugin's functionality to handle static assets too, for example copy lib\images\placeholder-1.jpg (but not placeholder-2.jpg) from library if it's used in html files, but I'm stuck at this point...
Here's the code of the plugin:
var CodeLibrary;
module.exports = CodeLibrary = (function() {
var required = [];
CodeLibrary.prototype.brunchPlugin = true;
function CodeLibrary(config) {
this.config = config;
this.pattern = /.*/;
this.watched = this.config.paths.watched.filter(function(path) {
return !path.match( config.plugins.library.pattern );
});
}
function is_required(path) {
var name = this.config.modules.nameCleaner(path);
return required.some(function(e, i, a) { return name.match(e); });
}
function in_library(path) {
return Boolean(path.match( this.config.plugins.library.pattern ));
}
function is_watched(path) {
return this.watched.some(function(e, i, a) { return path.match( e ); });
}
CodeLibrary.prototype.lint = function(data, path, callback) {
if( !is_watched.apply(this, [path]) &&
!is_required.apply(this, [path]) )
return callback();
var es6_pattern = /import .*'(.*)'/gm;
var commonjs_pattern = /require\('(.*)'\)/gm;
var match = es6_pattern.exec(data) || commonjs_pattern.exec(data);
while( match != null ) {
if( required.indexOf(match[1]) === -1 )
required.push( match[1] );
match = es6_pattern.exec(data) || commonjs_pattern.exec(data);
}
callback();
}
CodeLibrary.prototype.compile = function(params, callback) {
if( is_required.apply(this, [params.path]) ||
!in_library.apply(this, [params.path]) )
return callback(null, params);
return callback(null, null);
};
return CodeLibrary;
})();

nodejs child_process.spawn msdeploy.exe with space in dest site

I am trying to use child_process.spawn with msdeploy.exe to automate deployement of some applications in IIS.
Whenever i have a space in my dest site name this makes msdeploy crash.
var command = 'C:/Program Files/IIS/Microsoft Web Deploy V3/msdeploy.exe';
var args = [];
args.push('-verb=sync');
args.push('-source:iisApp=C:/Users/PATH_TO_DEPLOY/dist');
args.push('-dest:iisApp=Default Web Site/test');
var process = spawn(command,args);
process.stdout.on('data', function(data) { grunt.log.write(data) });
process.stderr.on('data', function(data) { grunt.log.error(data); });
process.on('exit', function(code) {
if (code !== 0) {
grunt.fail.warn('Something went wrong');
}
done();
});
I've tried some others alternative like put " '-dest:iisApp="Default Web Site/test"' but msdeploy give me an error too.
This error is like : Argument '"-dest:iisApp=Default Web Site/haha"' not recognized. All arguments must begin with "-" char.
When i try to escape the space char or put " like describe above this gave me a similar error.
Is this is a bug in nodejs ? Maybe i've made something wrong ?
Thank.
How to accomplish this:
var path = require('path');
var platform = require('platform');
var cp = require('child_process');
var full_cmd = '/path/to/dir with space/program.exe';
var cmd = '.' + path.sep + path.basename(full_cmd); // cannot include double quotes -- the work-around is to use the 'cmd_opts.cwd'
var cmd_args = ['"--import-path=/path/to/dir with space/import_file"']; // can wrap each with double-quotes (Windows only -- fails on Unix)
var cmd_opts = {
cwd: path.dirname(full_cmd),
encoding: 'utf8'
};
if (platform.os() === 'win32') {
cmd_opts.windowsVerbatimArguments = true;
}
var proc = cp.spawn(
cmd,
cmd_args,
cmd_opts
);
The only way this doesn't work is if 'program.exe' is named something like 'program name with space.exe'

Is 7zip stdout broken? Is there a way to capture the progress in nodejs? [Windows]

I am trying to get the stdout of 7zip when it processes files and get the percentage in nodeJs, but it doesn't behave as expected. 7zip doesn't output anything to stdout until the very end of the execution. Which is not very helpful.. especially when I have large files being compressed and no feedback is shown for a very long time.
The code I am using (simplified):
// 7zip test, place the 7z.exe in the same dir, if it's not on %PATH%
var cp = require('child_process');
var inputFile = process.argv[2]; if(inputFile==null) return;
var regProgress = /(\d{1,3})%\s*$/; //get the last percentage of the string, 3 digits
var proc = cp.spawn("7z.exe",["a","-t7z" ,"-y" ,inputFile + ".7z",inputFile]);
proc.stdout.setEncoding("utf8");
proc.stdout.on("data",function(data){
if(regProgress.test(data))
console.log("Progress = " + regProgress.exec(data)[1] + "%");
});
proc.once("exit",function(exit,sig){ console.log("Complete"); });
I have used the same code to get the percentage with WinRar successfully and I am beginning to think that 7zip might be buggy? Or I am doing it wrong? Can I forcefully read the stdout of a process with a timer perhaps?
The same code above, with the exception of the following line replaced, works as expected with WinRar.
var proc = cp.spawn("Rar.exe",["a","-s","-ma5","-o+",inputFile+".rar",inputFile]);
If anyone knows why this happens and if it is fixable, I would be grateful! :-)
p.s. I have tried 7za.exe, the command line version of 7zip, also the stable, beta and alpha versions, they all have the same issue
It is no longer needed to use a terminal emulator like pty.js, you can pass the -bsp1 to 7z to force to output the progress to stdout.
7-zip only outputs progress when stdout is a terminal.
To trick 7-zip, you need to npm install pty.js (requires Visual Studio or VS Express with Windows SDK) and then use code like:
var pty = require('pty');
var inputFile = process.argv[2],
pathTo7zip = 'c:\\Program Files\\7-Zip\\7z.exe';
if (inputFile == null)
return;
var term = pty.spawn(process.env.ComSpec, [], {
name: 'ansi',
cols: 200,
rows: 30,
cwd: process.env.HOME,
env: process.env
});
var rePrg = /(\d{1,3})%\r\n?/g,
reEsc = /\u001b\[\w{2}/g,
reCwd = new RegExp('^' + process.cwd().replace(/\\/g, '\\\\'), 'm');
prompts = 0,
buffer = '';
term.on('data', function(data) {
var m, idx;
buffer += data;
// remove terminal escape sequences
buffer = buffer.replace(reEsc, '');
// check for multiple progress indicators in the current buffer
while (m = rePrg.exec(buffer)) {
idx = m.index + m[0].length;
console.log(m[1] + ' percent done!');
}
// check for the cmd.exe prompt
if (m = reCwd.exec(buffer)) {
if (++prompts === 2) {
// command is done
return term.kill();
} else {
// first prompt is before we started the actual 7-zip process
if (idx === undefined) {
// we didn't see a progress indicator, so make sure to truncate the
// prompt from our buffer so that we don't accidentally detect the same
// prompt twice
buffer = buffer.substring(m.index + m[0].length);
return;
}
}
}
// truncate the part of our buffer that we're done processing
if (idx !== undefined)
buffer = buffer.substring(idx);
});
term.write('"'
+ pathTo7zip
+ '" a -t7z -y "'
+ inputFile
+ '.7z" "'
+ inputFile
+ '"\r');
It should be noted that 7-zip does not always output 100% at finish. If the file compresses quickly, you may just see only a single 57% for example, so you will have to handle that however you want.

Resources