diff --git a/README.md b/README.md index eda4567..a065497 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,8 @@ Meteorite is a Meteor version manager and package manager. It provides an easy way to run different versions of meteor, use non-core packages, and to install packages from the [Atmosphere package repository](https://atmosphere.meteor.com/). -Meteorite provides the command `mrt`, which can be used to add and install smart packages from atmosphere. +Meteorite provides two commands: `mrt` and `meteorite` (the latter was added to avoid naming conflict with Microsoft Malicious Software Removal Tool on Windows), which can be used to add and install smart packages from atmosphere. +These commands are absolutely identical, but if you're going to call Meteorite from your scripts, you'd better use `meteorite`, as it works both on Windows and Linux. ``` sh # Create an app based on Meteor's devel branch. @@ -37,11 +38,21 @@ Subsequently, you can simply use `meteor` to run your development server, and ju ### NOTES -- Meteor is not officially supported on windows; you can run it thanks to [Tom Wijman's excellent work](http://win.meteor.com). However, meteorite's git based approach runs counter to the MSI installation that's required to get it working. So meteorite *does not* work under windows right now. Pull Requests which change this would be gladly accepted! Also, see [this blog post](http://www.discovermeteor.com/2013/03/20/using-meteor-and-atmopshere-on-windows/) for some information about how to use it. - - You'll also need to ensure you have [git](http://git-scm.com) installed and available in your path. Also, you'll need to make sure that `mrt`'s install location (usually `/usr/local/bin/`) is on your path. +### Meteorite on Windows + +Meteor is not officially supported on windows; you can run it thanks to [Tom Wijman's excellent work](http://win.meteor.com). + +Meteorite can be installed and run as any NPM package. Several things are useful to know: + +- Meteor release pinning does not work on Windows, see example below for details. +- Meteorite keeps its files under `\AppData\Local\.meteorite`, just like Meteor do. +- Directory junctions are used as the best alternative to Linux's symlinks. This automatically implies that Meteorite won't work on FAT32 drives (but will on NTFS). +- Explorer in older Windows and another file managers may handle removal of directory junctions inaccurate, which may lead to deletion of all files inside the junction instead of just +breaking the link. Please be careful: if this happens, you will have to manually remove empty package folder from Meteorite's cache to make it re-download all files. + ## Usage ### `mrt add ` @@ -146,6 +157,8 @@ Meteorite writes to a `smart.lock` file in the app's root directory to track the The `meteor` property is not required: apps will depend on Meteor's master branch by default. You can specify `meteor.branch`, `meteor.tag` or `meteor.git` to use alternate branches, tags and forks respectively. Note that `meteor.git` expects an actual URL, use `ssh://git@github.com/meteor/meteor.git` instead of `git@github.com:meteor/meteor.git`. +Keep in mind that main Meteor repository doesn't work well on Windows yet and Meteor pinning is explicitly disabled for Windows users of Meteorite (default Meteor is always used). If you still want Meteorite to run your custom Meteor that works both on Windows and Linux, add `meteor.force-on-windows: true` parameter to `smart.json`. + ``` json { "packages": { diff --git a/lib/atmosphere.js b/lib/atmosphere.js index 8d86e56..97c182f 100644 --- a/lib/atmosphere.js +++ b/lib/atmosphere.js @@ -3,26 +3,29 @@ var Config = require('./config'); var path = require('path'); var fs = require('fs'); var _ = require('underscore'); -var spawn = require('child_process').spawn; +var spawn = require('spawn-cmd').spawn; var exec = require('child_process').exec; var git = require('./utils/git'); var _ = require('underscore'); var prompt = require('prompt'); var meteoriteArgs = require('optimist').argv; var version = require('../package').version; +var env = require('../lib/utils/env'); +var exit = require('exit'); var writeUserTokenFile = function(user) { var cfg = { user: user }; - var filePath = path.join(process.env.HOME, '.mrt.cfg'); + + var filePath = env.buildFromHomePath('.mrt.cfg'); fs.writeFileSync(filePath, JSON.stringify(cfg)); fs.chmodSync(filePath, '0600'); }; var readUserTokenFile = function() { - var filePath = path.join(process.env.HOME, '.mrt.cfg'); + var filePath = env.buildFromHomePath('.mrt.cfg'); var fileContents = fs.readFileSync(filePath); return JSON.parse(fileContents); @@ -57,7 +60,8 @@ Atmosphere = { }); } - process.exit(1); + exit(1); + return; } meteoriteArgs.verbose && console.log("Published."); fn(); @@ -179,7 +183,8 @@ Atmosphere = { console.log("Please ensure you are running the latest version of Meteorite".red); console.log(" npm install -g meteorite".red); console.log("If problems persist, please report here: http://github.com/oortcloud/meteorite/issues".red); - process.exit(1); + exit(1); + return; } meteoriteArgs.verbose && console.log("Connected.."); @@ -218,7 +223,8 @@ Atmosphere = { }, function (err, input) { if (err) { console.log('Error:', err.reason); - process.exit(1); + exit(1); + return; } meteoriteArgs.verbose && console.log("Got credentials, logging in.."); @@ -231,7 +237,8 @@ Atmosphere = { if (err) { console.log('Error:', err.reason); - process.exit(1); + exit(1); + return; } writeUserTokenFile(user); @@ -269,7 +276,8 @@ Atmosphere = { }], function(err, user) { if (err) { console.log('Error:', err.reason); - process.exit(1); + exit(1); + return; } meteoriteArgs.verbose && console.log("Logged in."); diff --git a/lib/command.js b/lib/command.js index 5bb8e85..73f28f8 100644 --- a/lib/command.js +++ b/lib/command.js @@ -1,4 +1,4 @@ -var spawn = require('child_process').spawn; +var spawn = require('spawn-cmd').spawn; var exec = require('child_process').exec; var path = require('path'); var fs = require('fs'); diff --git a/lib/dependencies/package.js b/lib/dependencies/package.js index f624785..217de2a 100755 --- a/lib/dependencies/package.js +++ b/lib/dependencies/package.js @@ -2,6 +2,7 @@ var _ = require('underscore'); var path = require('path'); var fs = require('fs'); var Source = require('../sources/source'); +var detectEol = require('../eol').detectEol; // A package represents a single version of a single package // @@ -126,7 +127,8 @@ Package.prototype.installInto = function(project, fn) { // Make link if it doesn't exist, inform caller if (! linkedTo) { - fs.symlinkSync(libPath, packagePath); + // Windows uses junctions to solve for symlinks. They need to be full paths too. + fs.symlinkSync(path.resolve(libPath), path.resolve(packagePath), 'junction'); self.addToGitIgnore(project); if (self._fromAtmosphere) { @@ -151,13 +153,14 @@ Package.prototype.addToGitIgnore = function(project) { if (fs.existsSync(gitignorePath)) gitignoreContent = fs.readFileSync(gitignorePath, 'utf8'); - var lines = gitignoreContent.split('\n'); + var eol = detectEol(gitignoreContent); + var lines = gitignoreContent.split(eol); if (! _.any(lines, function(l) { return l === "/" + self.name })) { lines.push("/" + self.name); lines = _.filter(lines, function(l) { return l !== ''}); - gitignoreContent = lines.join('\n') + '\n'; + gitignoreContent = lines.join(eol) + eol; fs.writeFileSync(gitignorePath, gitignoreContent); } } diff --git a/lib/eol.js b/lib/eol.js new file mode 100644 index 0000000..22fbe61 --- /dev/null +++ b/lib/eol.js @@ -0,0 +1,21 @@ +var os = require('os'); + +// Users may have UNIX line endings on Windows if their git is configured +// in that way, see https://github.com/oortcloud/meteorite/pull/224#issuecomment-34903926 +eol = { + detectEol: function(data) { + data = data || ''; + var eols = [ '\r\n', '\r', '\n' ]; + var result = os.EOL; + for (var i = 0; i < eols.length; i++) { + var eol = eols[i]; + if (data.indexOf(eol) >= 0) { + result = eol; + break; + } + }; + return result; + } +} + +module.exports = eol; diff --git a/lib/meteor.js b/lib/meteor.js index 553df66..e3db53d 100644 --- a/lib/meteor.js +++ b/lib/meteor.js @@ -5,9 +5,11 @@ var Command = require('./command'); var fs = require('fs'); var fstream = require('fstream'); var wrench = require('wrench'); -var spawn = require('child_process').spawn; +var spawn = require('spawn-cmd').spawn; var exec = require('child_process').exec; +var which = require('which'); +var customMeteorWarning = false; // A 'Meteor' refers to a single commit (branch, tag) of a version of the core meteor // @@ -18,6 +20,15 @@ Meteor = function(config) { // Config defaults config = config || {}; this.defaultMeteor = (! _.include(_.keys(config), 'git')); + + if (!this.defaultMeteor && !config['force-on-windows'] && process.platform == 'win32') { + if (!customMeteorWarning) { + console.log('Custom Meteor build most probably won\' work on Windows, using default instead'.red.bold); + console.log('Please refer to the README.md for details'.red.bold); + customMeteorWarning = true; + } + this.defaultMeteor = true; + } if (this.defaultMeteor) { this.prepared = true @@ -79,7 +90,7 @@ Meteor.prototype.install = function(buildDevBundle, fn) { } else { // meteor --help installs the dev bundle before doing anything else console.log('Downloading Meteor development bundle'); - var meteor = spawn('./meteor', ['--help'], {cwd: self.source.path}); + var meteor = spawn(self._executable(), ['--help'], {cwd: self.source.path}); // just output the status bar meteor.stderr.pipe(process.stderr); @@ -149,7 +160,7 @@ Meteor.prototype.hasPackage = function(pkgName, fn) { if (!self.packageNames) { // we have to call meteor list because there's no obvious place to look - exec('meteor list', function(error, list) { + exec(self._executable() + ' list', function(error, list) { if (error) { throw "Error running a command: " + error; } diff --git a/lib/meteorite.js b/lib/meteorite.js index 967e975..54b5984 100644 --- a/lib/meteorite.js +++ b/lib/meteorite.js @@ -4,6 +4,8 @@ var Atmosphere = require('../lib/atmosphere'); var fs = require('fs'); var _ = require('underscore'); var wrench = require('wrench'); +var env = require('../lib/utils/env'); +var os = require('os'); var OUR_ARGS = ['--verbose', '--repoHost', '--repoPort', '--build-dev-bundle']; @@ -63,7 +65,7 @@ Meteorite.prototype['create-package'] = function(fn) { // write package.js fs.writeFileSync(path.join(packageDir, 'package.js'), - 'Package.describe({\n' + + ('Package.describe({\n' + ' summary: "REPLACEME - What does this package (or the original one you\'re wrapping) do?"\n' + '});\n\n' + 'Package.on_use(function (api, where) {\n' + @@ -73,13 +75,15 @@ Meteorite.prototype['create-package'] = function(fn) { ' api.use(\'' + packageName + '\');\n\n' + ' api.add_files(\'' + testFile + '\', [\'client\', \'server\']);\n' + '});\n' + ).replace(/\n/g, os.EOL) ); // touch relevant files fs.writeFileSync(path.join(packageDir, packageFile), ''); fs.writeFileSync(path.join(packageDir, testFile), - '// See https://github.com/dandv/meteor-crypto-base64/blob/master/crypto-base64_tests.js for a simple example' + - '// See https://www.eventedmind.com/feed/e6gJZXNQWyNP2MLsb for more on testing with Tinytest' + ('// See https://github.com/dandv/meteor-crypto-base64/blob/master/crypto-base64_tests.js for a simple example\n' + + '// See https://www.eventedmind.com/feed/e6gJZXNQWyNP2MLsb for more on testing with Tinytest\n' + ).replace(/\n/g, os.EOL) ); // write simple smart.json @@ -91,7 +95,7 @@ Meteorite.prototype['create-package'] = function(fn) { "version": "0.0.1", "git": "", "packages": {} - }, null, 2)); + }, null, 2).replace(/\n/g, os.EOL)); } // XXX: this should probably track this (so further calls to mrt install @@ -114,9 +118,10 @@ Meteorite.prototype['link-package'] = function(fn) { // pointing to the wrong spot if (old) - fs.unlinkSync(packagePath); + fs.unlinkSync(path.resolve(packagePath)); - fs.symlinkSync(packageDir, packagePath); + // Windows uses junctions to solve for symlinks. They need to be full paths too. + fs.symlinkSync(path.resolve(packageDir), path.resolve(packagePath), 'junction'); } /////// Package level meteorite commands @@ -236,8 +241,7 @@ _.each([ // Class methods Meteorite.root = function() { - var homeDir = process.env.HOME; - return path.join(homeDir, '.meteorite'); + return env.buildFromHomePath('.meteorite'); }; // Creates the path to ~/.meteorite diff --git a/lib/project.js b/lib/project.js index 550129e..d3badf6 100644 --- a/lib/project.js +++ b/lib/project.js @@ -7,6 +7,8 @@ var Meteor = require('./meteor'); var Command = require('./command'); var wrench = require('wrench'); var exec = require('child_process').exec; +var exit = require('exit'); +var detectEol = require('./eol').detectEol; // The project is the current directory's personal version of meteor, // complete with its own set of packages. @@ -20,6 +22,13 @@ Project = function(root, meteorArgs) { this.smartJsonPath = path.join(this.root, 'smart.json'); this.smartLockPath = path.join(this.root, 'smart.lock'); this.packagesRoot = path.join(this.root, 'packages'); + + var data = ''; + if (fs.existsSync(this.smartLockPath)) + data += fs.readFileSync(this.smartLockPath).toString(); + if (fs.existsSync(this.smartJsonPath)) + data += fs.readFileSync(this.smartJsonPath).toString(); + this.EOL = detectEol(data); // set a base meteor if it's specified in the args (or a default one if not) this.meteor = new Meteor(meteorArgs); @@ -102,7 +111,8 @@ Project.prototype.fetch = function(fn, forceUpdate) { if (err) { console.log(err.message.red); - process.exit(1); + exit(1); + return; } fn(); @@ -319,7 +329,7 @@ Project.prototype.writeSmartJson = function(json) { // Write to disk if (fs.existsSync(this.root)) - fs.writeFileSync(this.smartJsonPath, smartJsonString); + fs.writeFileSync(this.smartJsonPath, smartJsonString.replace(/\n/g, this.EOL)); }; Project.prototype.lockJson = function() { @@ -334,7 +344,7 @@ Project.prototype.lockJson = function() { Project.prototype.writeLockFile = function() { var smartJsonString = JSON.stringify(this.lockJson(), null, 2) + "\n"; - fs.writeFileSync(this.smartLockPath, smartJsonString); + fs.writeFileSync(this.smartLockPath, smartJsonString.replace(/\n/g, this.EOL)); }; Project.prototype._optimizeFS = function() { diff --git a/lib/sources/git.js b/lib/sources/git.js index 3f041de..4337f58 100644 --- a/lib/sources/git.js +++ b/lib/sources/git.js @@ -3,7 +3,7 @@ var path = require('path'); var wrench = require('wrench'); var fs = require('fs'); var url = require('url'); -var spawn = require('child_process').spawn; +var spawn = require('spawn-cmd').spawn; var exec = require('rolling_timeout_exec').exec; var fstream = require('fstream'); @@ -118,6 +118,7 @@ GitSource.prototype._clone = function(fn) { options = { rollingTimeout: ROLLING_TIMEOUT }; if (!fs.existsSync(this.sourcePath)) { + wrench.mkdirSyncRecursive(this.sourcePath); child = exec(command, options, function(err, stdout, stderr) { if (err) { reportErrors(err, stdout, stderr, timedOut); diff --git a/lib/utils/env.js b/lib/utils/env.js new file mode 100644 index 0000000..9d27519 --- /dev/null +++ b/lib/utils/env.js @@ -0,0 +1,13 @@ +var path = require('path'); + +Env = { + buildFromHomePath: function(subPath) { + var homeDir = process.env.HOME + if (process.platform == 'win32') { + var homeDir = process.env.LOCALAPPDATA || process.env.APPDATA; + } + return path.join(homeDir, subPath); + } +}; + +module.exports = Env; \ No newline at end of file diff --git a/package.json b/package.json index 0b71f8d..58a912b 100644 --- a/package.json +++ b/package.json @@ -7,17 +7,21 @@ , "dependencies": { "ddp": ">=0.3.1" , "underscore": "1.3.3" - , "wrench": "1.3.9" + , "wrench": ">=1.5.5" , "fstream": ">=0.1.18" , "optimist": ">=0.3.4" , "prompt": "0.2.11" , "colors": "0.6.0-1" , "async": "0.2.9" , "rolling_timeout_exec": ">=0.0.1" + , "which": ">=1.0.5" + , "exit": ">=0.1.2" + , "spawn-cmd": ">=0.0.2" } , "devDependencies": { "mocha": ">=1.2.2" , "connect": ">=2.3.6" + , "wrench": ">=1.5.5" , "which": ">=1.0.5" } , "scripts": @@ -29,5 +33,5 @@ { "type" : "git" , "url" : "https://github.com/oortcloud/meteorite.git" } -, "bin" : { "mrt" : "./bin/mrt.js" } +, "bin" : { "mrt" : "./bin/mrt.js", "meteorite" : "./bin/mrt.js" } } diff --git a/spec/acceptance/run_spec.js b/spec/acceptance/run_spec.js index cf24e02..b408e3a 100644 --- a/spec/acceptance/run_spec.js +++ b/spec/acceptance/run_spec.js @@ -104,29 +104,33 @@ describe('invoking `mrt run`', function() { }); }); - describe('and the smart.json specifies a meteor fork pinned to a branch', function() { - it("should run the forked meteor checked out to the branch", function(done) { - runner.invokeMrtInApp('app-with-meteor-pinned-to-branch', ['run'], { - waitForOutput: "Meteor is instrumented for meteorite test (branch/mrt-branch-test)" - }, done); + if (process.platform == 'win32') { + console.log('Skipping Meteor pinning tests because this is not expected to work on Windows'.yellow.bold); + } else { + describe('and the smart.json specifies a meteor fork pinned to a branch', function() { + it("should run the forked meteor checked out to the branch", function(done) { + runner.invokeMrtInApp('app-with-meteor-pinned-to-branch', ['run'], { + waitForOutput: "Meteor is instrumented for meteorite test (branch/mrt-branch-test)" + }, done); + }); }); - }); - describe('and the smart.json specifies a meteor fork pinned to a tag', function() { - it("should run the forked meteor checked out to the branch", function(done) { - runner.invokeMrtInApp('app-with-meteor-pinned-to-tag', ['run'], { - waitForOutput: "Meteor is instrumented for meteorite test (tag/mrt-test-tag)" - }, done); + describe('and the smart.json specifies a meteor fork pinned to a tag', function() { + it("should run the forked meteor checked out to the branch", function(done) { + runner.invokeMrtInApp('app-with-meteor-pinned-to-tag', ['run'], { + waitForOutput: "Meteor is instrumented for meteorite test (tag/mrt-test-tag)" + }, done); + }); }); - }); - describe('and the smart.json specifies a meteor fork pinned to a ref', function() { - it("should run the forked meteor checked out to the branch", function(done) { - runner.invokeMrtInApp('app-with-meteor-pinned-to-ref', ['run'], { - waitForOutput: "Meteor is instrumented for meteorite test (ref)" - }, done); + describe('and the smart.json specifies a meteor fork pinned to a ref', function() { + it("should run the forked meteor checked out to the branch", function(done) { + runner.invokeMrtInApp('app-with-meteor-pinned-to-ref', ['run'], { + waitForOutput: "Meteor is instrumented for meteorite test (ref)" + }, done); + }); }); - }); + } }); }); diff --git a/spec/acceptance/test_spec.js b/spec/acceptance/test_spec.js index 715545f..aed940f 100644 --- a/spec/acceptance/test_spec.js +++ b/spec/acceptance/test_spec.js @@ -6,6 +6,7 @@ var ddp = require('ddp'); var which = require('which'); var utils = require('../lib/utils.js'); +var env = require('../../lib/utils/env.js'); var atmosphere = require('../lib/atmosphere.js'); // this is our home dir for running mrt against @@ -17,19 +18,23 @@ var appDir = path.join(appHome, 'app'); before(function(done){ // ensure our "cached" git is in the path process.env._METEORITE_REAL_GIT = which.sync('git'); - process.env.PATH = [path.resolve(path.join('spec', 'support', 'bin')), process.env.PATH].join(':'); - - // make sure Meteor doesn't try to install into our soon to be clean home dir - process.env.METEOR_WAREHOUSE_DIR = path.join(process.env.HOME, '.meteor'); + process.env._METEORITE_REAL_METEOR = which.sync('meteor'); + process.env.PATH = [path.resolve(path.join('spec', 'support', 'bin')), process.env.PATH].join(path.delimiter); + + if (!process.env.METEOR_WAREHOUSE_DIR) { + // make sure Meteor doesn't try to install into our soon to be clean home dir + process.env.METEOR_WAREHOUSE_DIR = env.buildFromHomePath('.meteor'); + } // set our home dir so we can easily blow away meteorite installs wrench.mkdirSyncRecursive(appHome); process.env.HOME = appHome; + process.env.LOCALAPPDATA = appHome; console.log("Preparing..") // ensure we have the latest dev bundle cached console.log(" Ensuring we have the dev bundle for system meteor"); - utils.downloadDevBundle('meteor', function() { + utils.downloadDevBundle(process.env._METEORITE_REAL_METEOR, function() { // ensure we have dev bundles for all our meteor forks // XXX: // for meteor in meteors/ diff --git a/spec/lib/atmosphere.js b/spec/lib/atmosphere.js index 0b5e1c4..b4f90a0 100644 --- a/spec/lib/atmosphere.js +++ b/spec/lib/atmosphere.js @@ -6,7 +6,7 @@ var async = require('async'); var assert = require('assert'); -var baseCommand = path.resolve(path.join('bin', 'mrt.js')) + ' --repoHost localhost --repoPort 3333 --repoUsername test --repoPassword testtest'; +var baseCommand = '"' + require('./env').meteoriteExecutable + '" --repoHost localhost --repoPort 3333 --repoUsername test --repoPassword testtest' var packagesNeeded = { 'mrt-test-pkg1': ['0.1.0', '0.2.0'], diff --git a/spec/lib/env.js b/spec/lib/env.js new file mode 100644 index 0000000..e2c7acf --- /dev/null +++ b/spec/lib/env.js @@ -0,0 +1,13 @@ +var path = require('path'); + +if (process.platform === 'win32') { + // Slashes are automatically normalized by path.resolve on Windows + shortPath = 'spec/support/bin/mrt.bat'; +} else { + shortPath = 'bin/mrt.js'; +}; + +Env = { + meteoriteExecutable: path.resolve(shortPath) +} +module.exports = Env; diff --git a/spec/lib/runner.js b/spec/lib/runner.js index a59afee..4ad5bc0 100644 --- a/spec/lib/runner.js +++ b/spec/lib/runner.js @@ -1,15 +1,14 @@ // run mrt against a certain app and wait for given output var path = require('path'); -var spawn = require('child_process').spawn; +var spawn = require('spawn-cmd').spawn; var exec = require('child_process').exec; var _ = require('underscore'); var utils = require('./utils.js'); var wrench = require('wrench'); +var which = require('which'); var fstream = require('fstream'); -var meteoriteExecutable = path.resolve(path.join('bin', 'mrt.js')); - var matchesSpecs = function(output, specs) { return _.all(specs, function(spec) { return output.indexOf(spec) > -1; @@ -18,6 +17,13 @@ var matchesSpecs = function(output, specs) { // kill the process family described by process, then send done the error var killProcessFamily = function(child, error, done) { + if (process.platform === 'win32') { + // We do not use /PID , because it will fail if process was already terminated + exec('taskkill /F /T /FI "PID eq ' + child.pid + '"', function(killErr) { + done(error || killErr); + }); + return; + } // we need to grab a table of process ids and parent ids to form a tree out of exec('ps -opid -oppid', function(err, rawData) { // parse the list of process ids and parent ids @@ -109,7 +115,7 @@ var invokeMrt = function(directory, args, options, done) { args.push('--repoHost=localhost'); args.push('--port=4444'); - spawnAndWait(meteoriteExecutable, args, options, done); + spawnAndWait(require('./env').meteoriteExecutable, args, options, done); } var invokeMrtInApp = function(appName, args, options, done) { diff --git a/spec/lib/utils.js b/spec/lib/utils.js index 3f85501..7bd749a 100644 --- a/spec/lib/utils.js +++ b/spec/lib/utils.js @@ -1,5 +1,5 @@ // basic filesystem utils for our tests -var spawn = require('child_process').spawn; +var spawn = require('spawn-cmd').spawn; var path = require('path'); var fs = require('fs'); var _ = require('underscore'); diff --git a/spec/support/bin/git b/spec/support/bin/git index 3ec6a3c..b8a1088 100755 --- a/spec/support/bin/git +++ b/spec/support/bin/git @@ -1,10 +1,11 @@ #!/usr/bin/env node -var spawn = require('child_process').spawn; +var spawn = require('spawn-cmd').spawn; var path = require('path'); var fs = require('../../../lib/utils/fs'); var wrench = require('wrench'); var _ = require('underscore'); +var exit = require('exit'); var args = process.argv.slice(2); @@ -12,13 +13,16 @@ var command = _.first(args); var copyRepo = function(from, to) { wrench.mkdirSyncRecursive(path.dirname(to)); - spawn('cp', ['-r', from, to]); + wrench.copyDirSyncRecursive(from, to, { + forceDelete: true // Directory 'to' may already exists + }); }; var REAL_GIT = process.env._METEORITE_REAL_GIT; if (!REAL_GIT) { console.error('No _METEORITE_REAL_GIT specified'); - process.exit(1); + exit(1); + return; } if (command === 'clone') { @@ -37,6 +41,7 @@ if (command === 'clone') { if (!fs.existsSync(repoCachePath)) { args.push(repoUrl); args.push(repoCachePath); + wrench.mkdirSyncRecursive(repoCachePath); var git = spawn(REAL_GIT, args); git.on('exit', function() { diff --git a/spec/support/bin/git.bat b/spec/support/bin/git.bat new file mode 100644 index 0000000..b3a4078 --- /dev/null +++ b/spec/support/bin/git.bat @@ -0,0 +1 @@ +@node "%~dp0\git" %* diff --git a/spec/support/bin/mrt.bat b/spec/support/bin/mrt.bat new file mode 100644 index 0000000..40f6366 --- /dev/null +++ b/spec/support/bin/mrt.bat @@ -0,0 +1,2 @@ +@REM This file is required for testing purposes only +@node "%~dp0\..\..\..\bin\mrt.js" %* diff --git a/spec/unit/resolver_spec.js b/spec/unit/resolver_spec.js index a86924a..d70d4c3 100644 --- a/spec/unit/resolver_spec.js +++ b/spec/unit/resolver_spec.js @@ -2,6 +2,7 @@ var _ = require('underscore'); var assert = require('assert'); var Meteorite = require('../../lib/meteorite'); var Dependencies = require('../../lib/dependencies/dependencies'); +var path = require('path'); require('../lib/mocks/package_mock'); require('../lib/mocks/atmosphere_mock'); @@ -33,41 +34,41 @@ var testConflict = function(pkgDefns, fn) { describe('The Resolver', function() { describe('with two local dependencies', function() { - testConflict({A: {path: '/A'}, B: {path: '/B'}}, + testConflict({A: {path: path.resolve('/A')}, B: {path: path.resolve('/B')}}, function(dependencies, done) { - assert.equal(dependencies.packages['C'].source.path, '/C.path.A'); + assert.equal(dependencies.packages['C'].source.path, path.resolve('/C.path.A')); done(); }); }); describe('with a local dependency vs a git one', function() { - testConflict({A: {path: '/A'}, B: {git: 'B'}}, + testConflict({A: {path: path.resolve('/A')}, B: {git: 'B'}}, function(dependencies, done) { - assert.equal(dependencies.packages['C'].source.path, '/C.path.A'); + assert.equal(dependencies.packages['C'].source.path, path.resolve('/C.path.A')); done(); }); }); describe('with a git dependency vs a local one', function() { - testConflict({A: {git: 'A'}, B: {path: '/B'}}, + testConflict({A: {git: 'A'}, B: {path: path.resolve('/B')}}, function(dependencies, done) { - assert.equal(dependencies.packages['C'].source.path, '/C.path.B'); + assert.equal(dependencies.packages['C'].source.path, path.resolve('/C.path.B')); done(); }); }); describe('with a local dependency vs an atmos one', function() { - testConflict({A: {path: '/A'}, B: {}}, + testConflict({A: {path: path.resolve('/A')}, B: {}}, function(dependencies, done) { - assert.equal(dependencies.packages['C'].source.path, '/C.path.A'); + assert.equal(dependencies.packages['C'].source.path, path.resolve('/C.path.A')); done(); }); }); describe('with a atmos dependency vs a local one', function() { - testConflict({A: {}, B: {path: '/B'}}, + testConflict({A: {}, B: {path: path.resolve('/B')}}, function(dependencies, done) { - assert.equal(dependencies.packages['C'].source.path, '/C.path.B'); + assert.equal(dependencies.packages['C'].source.path, path.resolve('/C.path.B')); done(); }); }); diff --git a/spec/unit/smart_lock_spec.js b/spec/unit/smart_lock_spec.js index 34cb6f1..3679ca7 100644 --- a/spec/unit/smart_lock_spec.js +++ b/spec/unit/smart_lock_spec.js @@ -2,6 +2,7 @@ var _ = require('underscore'); var assert = require('assert'); var Meteorite = require('../../lib/meteorite'); var Dependencies = require('../../lib/dependencies/dependencies'); +var path = require('path'); require('../lib/mocks/package_mock'); require('../lib/mocks/project_mock'); @@ -126,17 +127,17 @@ describe('Writing smart.lock', function() { expected = { packages: { "mrt-test-pkg2": { - "path": "../../mrt-test-pkg2" + "path": path.normalize("../../mrt-test-pkg2") }, "mrt-test-pkg1": { // pkg2 has a link to ../mrt-test-pkg1, so when it resolves, it // should be two levels up - "path": "../../mrt-test-pkg1" + "path": path.normalize("../../mrt-test-pkg1") } }, basePackages: { "mrt-test-pkg2": { - "path": "../../mrt-test-pkg2" + "path": path.normalize("../../mrt-test-pkg2") } } };