diff --git a/build/_bower.shade b/build/_bower.shade new file mode 100644 index 0000000000..e8392184be --- /dev/null +++ b/build/_bower.shade @@ -0,0 +1,9 @@ +default currentDir = '${Directory.GetCurrentDirectory()}' +default nodeDir = '${Path.Combine(currentDir, "bin", "nodejs")}' +var bowerInstalled = '${Directory.Exists(Path.Combine(nodeDir, "node_modules", "bower"))}' + +- // Install bower locally +npm npmCommand='install --prefix ${nodeDir} bower' if='!bowerInstalled' once='installBower' + +- // Run bower +node nodeCommand='${Path.Combine(nodeDir, "node_modules", "bower", "bin", "bower")} ${bowerCommand}' workingdir='${bowerDir}' \ No newline at end of file diff --git a/build/_grunt.shade b/build/_grunt.shade new file mode 100644 index 0000000000..05f892ab95 --- /dev/null +++ b/build/_grunt.shade @@ -0,0 +1,9 @@ +default currentDir = '${Directory.GetCurrentDirectory()}' +default nodeDir = '${Path.Combine(currentDir, "bin", "nodejs")}' +var gruntCliInstalled = '${Directory.Exists(Path.Combine(nodeDir, "node_modules", "grunt-cli"))}' + +-// Install grunt-cli locally +npm npmCommand='install --prefix ${nodeDir} grunt-cli' if='!gruntCliInstalled' once='installGruntCli' + +-// Run grunt +node nodeCommand='${Path.Combine(nodeDir, "node_modules", "grunt-cli", "bin", "grunt")}' workingdir='${gruntDir}' \ No newline at end of file diff --git a/build/_k-standard-goals.shade b/build/_k-standard-goals.shade index 30b40b5488..a7f867ad20 100644 --- a/build/_k-standard-goals.shade +++ b/build/_k-standard-goals.shade @@ -74,9 +74,33 @@ default Configuration='Release' } } +#restore-npm-modules + -// Find all dirs that contain a package.json file + var npmDirs = '${GetDirectoriesContaining(Directory.GetCurrentDirectory(), "package.json")}' + npm npmCommand='install' each='var npmDir in npmDirs' + +#restore-bower-components + -// Find all dirs that contain a bower.json file + var bowerDirs = '${GetDirectoriesContaining(Directory.GetCurrentDirectory(), "bower.json")}' + bower each='var bowerDir in bowerDirs' bowerCommand='install' + +#run-grunt .restore-npm-modules .restore-bower-components target='compile' + -// Find all dirs that contain a gruntfile.js file + var gruntDirs = '${GetDirectoriesContaining(Directory.GetCurrentDirectory(), "gruntfile.js")}' + grunt each='var gruntDir in gruntDirs' + functions @{ string E(string key) { return Environment.GetEnvironmentVariable(key); } void E(string key, string value) { Environment.SetEnvironmentVariable(key, value); } + IEnumerable GetDirectoriesContaining(string path, string searchPattern) { + var sep = Path.DirectorySeparatorChar; + // Don't include directories that are children of a node_modules or bower_components directory + return Directory.GetFiles(path, searchPattern, SearchOption.AllDirectories) + .Where(p => p.IndexOf(sep + "node_modules" + sep) < 0 && + p.IndexOf(sep + "bower_components" + sep) < 0) + .Select(p => Path.GetDirectoryName(p)) + .Distinct(); + } } macro name='Exec' program='string' commandline='string' diff --git a/build/_node-install.shade b/build/_node-install.shade new file mode 100644 index 0000000000..609d0b1cd6 --- /dev/null +++ b/build/_node-install.shade @@ -0,0 +1,48 @@ +use assembly="System.IO.Compression.FileSystem, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" +use namespace="System.IO" +use namespace="System.IO.Compression" +use namespace="System.Net" + +default binDir = '${Path.Combine(Directory.GetCurrentDirectory(), "bin")}' + +-// When updating the node version you need to update the nodeExeSha to match what 'signtool.exe /pa /v node.exe' emits +default nodeVer = '0.10.28' +default npmVer = '1.4.9' +default nodeExeSha = '628FFD6C3577068C00CEC9F6F897F0EC8F5212D9' +default nodeInstallDir = '${Path.Combine(binDir, "nodejs")}' + +var nodeExe = 'node.exe' +var npmZip = 'npm-${npmVer}.zip' +var nodeDist = 'http://nodejs.org/dist/' +var nodeUrl = '${nodeDist}v${nodeVer}/${nodeExe}' +var npmUrl = '${nodeDist}npm/${npmZip}' +var doInstall = '${!File.Exists(nodeInstallDir)}' +var nodeInstallExePath = '${Path.Combine(nodeInstallDir, nodeExe)}' +var npmInstallZipPath = '${Path.Combine(nodeInstallDir, npmZip)}' + +@{ + if (doInstall) { + Log.Info("Installing nodejs locally"); + + if (Directory.Exists(nodeInstallDir)) + { + Directory.Delete(nodeInstallDir, recursive: true); + } + Directory.CreateDirectory(nodeInstallDir); + + // Download node + var wc = new WebClient(); + + Log.Info(string.Format("Downloading {0} to {1}", nodeUrl, nodeInstallExePath)); + wc.DownloadFile(nodeUrl, nodeInstallExePath); + + Log.Info(string.Format("Downloading {0} to {1}", npmUrl, npmInstallZipPath)); + wc.DownloadFile(npmUrl, npmInstallZipPath); + + // Unzip npm + Log.Info(string.Format("Unzipping npm to {0}", nodeInstallDir)); + ZipFile.ExtractToDirectory(npmInstallZipPath, nodeInstallDir); + } +} + +verify-authenticode verifyFilePath='${nodeInstallExePath}' expectedHash='${nodeExeSha}' if='doInstall' \ No newline at end of file diff --git a/build/_node.shade b/build/_node.shade new file mode 100644 index 0000000000..1e4af244f7 --- /dev/null +++ b/build/_node.shade @@ -0,0 +1,6 @@ +default currentDir = '${Directory.GetCurrentDirectory()}' +default nodeDir = '${Path.Combine(currentDir, "bin", "node")}' +var nodeExePath = '${Path.Combine(nodeDir, "node.exe")}' + +node-install once='installNode' +exec program='${nodeExePath}' commandline='${nodeCommand}' \ No newline at end of file diff --git a/build/_npm.shade b/build/_npm.shade new file mode 100644 index 0000000000..68daf9793e --- /dev/null +++ b/build/_npm.shade @@ -0,0 +1,7 @@ +default currentDir = '${Directory.GetCurrentDirectory()}' +default nodeDir = '${Path.Combine(currentDir, "bin", "nodejs")}' +default npmDir = '${currentDir}' +var npmCmd = '${Path.Combine(nodeDir, "npm.cmd")}' + +node-install once='installNode' +exec program='${npmCmd}' commandline='${npmCommand}' workingdir='${npmDir}' \ No newline at end of file diff --git a/build/_verify-authenticode.shade b/build/_verify-authenticode.shade new file mode 100644 index 0000000000..3873f64b16 --- /dev/null +++ b/build/_verify-authenticode.shade @@ -0,0 +1,44 @@ +use namespace="System.Diagnostics" +use namespace="System.IO" + +default workingdir='${Directory.GetCurrentDirectory()}' +default expectedHash = '' + +@{ + var signToolExe = "signtool.exe"; + var processStartInfo = new ProcessStartInfo { + UseShellExecute = false, + WorkingDirectory = workingdir, + FileName = signToolExe, + RedirectStandardOutput = true, + Arguments = "verify /pa /v " + verifyFilePath, + }; + + Log.Info(string.Format("Verifying Authenticode signature of {0}", verifyFilePath)); + + var signTool = Process.Start(processStartInfo); + var stdout = signTool.StandardOutput.ReadToEnd(); + signTool.WaitForExit(); + + if (signTool.ExitCode != 0) + { + File.Delete(verifyFilePath); + throw new Exception(string.Format("The signature verification for {0} failed:{1}{2}", verifyFilePath, Environment.NewLine, stdout)); + } + + if (!string.IsNullOrEmpty(expectedHash)) + { + // SHA1 of the file is on 4th line and looks like: Hash of file (sha1): 628FFD6C3577068C00CEC9F6F897F0EC8F5212D9 + var lines = stdout.Split(new [] { Environment.NewLine }, StringSplitOptions.None); + var hashLine = lines[3]; + var actualHash = hashLine.Substring(hashLine.IndexOf(":") + 1).Trim(); + + if (!string.Equals(expectedHash, actualHash, StringComparison.Ordinal)) + { + File.Delete(verifyFilePath); + throw new Exception(string.Format("The hash comparison for {0} failed: expected hash '{1}', actual hash '{2}'", verifyFilePath, expectedHash, actualHash)); + } + } + + Log.Info("Authenticode signature verified!"); +} \ No newline at end of file