Comparing version 0.6.3 to 0.7.0
@@ -66,3 +66,3 @@ #!/usr/bin/env node | ||
else if (cli_opts.uninstall) { | ||
plugman.uninstall(cli_opts.platform, cli_opts.project, cli_opts.plugin, plugins_dir, cli_opts.www); | ||
plugman.uninstall(cli_opts.platform, cli_opts.project, cli_opts.plugin, plugins_dir, {}, cli_opts.www); | ||
} | ||
@@ -78,3 +78,3 @@ else { | ||
} | ||
plugman.install(cli_opts.platform, cli_opts.project, cli_opts.plugin, plugins_dir, cli_variables, cli_opts.www); | ||
plugman.install(cli_opts.platform, cli_opts.project, cli_opts.plugin, plugins_dir, '.', cli_variables, cli_opts.www); | ||
} | ||
@@ -81,0 +81,0 @@ |
@@ -5,3 +5,3 @@ { | ||
"description": "install/uninstall Cordova plugins", | ||
"version": "0.6.3", | ||
"version": "0.7.0", | ||
"repository": { | ||
@@ -23,3 +23,6 @@ "type": "git", | ||
"shelljs": "0.1.x", | ||
"osenv": "0.0.x" | ||
"osenv": "0.0.x", | ||
"ncallbacks":"1.1.0", | ||
"underscore":"1.4.4", | ||
"dep-graph":"1.1.0" | ||
}, | ||
@@ -26,0 +29,0 @@ "devDependencies": { |
@@ -24,5 +24,4 @@ /* | ||
uninstall:require('./src/uninstall'), | ||
remove: require('./src/remove'), | ||
fetch: require('./src/fetch'), | ||
prepare: require('./src/prepare') | ||
}; |
@@ -235,2 +235,15 @@ # plugman | ||
### <dependency> | ||
Dependency tags let you specify plugins on which this plugin depends. In the future there will be plugin repositories to fetch plugins from. In the short term, plugins are directly pointed to by URLs in `<dependency>` tags. These tags have the following format: | ||
<dependency id="com.plugin.id" src="https://github.com/myuser/someplugin" commit="428931ada3891801" subdir="some/path/here" /> | ||
* `id`: gives the ID of the plugin. This should be globally unique, and in reverse-domain style. Neither of these restrictions is currently enforced, but they may be in the future and plugins should still follow them. | ||
* `src`: A URL for the plugin. This should point to a git repository, since plugman will try to `git clone` it. | ||
* `commit`: This is any git ref. It can be a branch or tag name (eg. `master`, `0.3.1`), a commit hash (eg. `975ddb228af811dd8bb37ed1dfd092a3d05295f9`), anything understood by `git checkout`. | ||
* `subdir`: Specifies that the plugin we're interested in exists as a subdirectory of the git repository. This is helpful because it allows one to keep several related plugins in a sigle git repository, and specify the plugins in it individually. | ||
In the future, version constraints will be introduced, and a plugin repository will exist to support fetching by name instead of explicit URLs. | ||
### <platform> | ||
@@ -237,0 +250,0 @@ |
@@ -11,3 +11,3 @@ var fetch = require('../src/fetch'), | ||
describe('fetch', function() { | ||
var copied_plugin_path = path.join(temp,'ChildBrowser'); | ||
var copied_plugin_path = path.join(temp, 'com.phonegap.plugins.childbrowser'); | ||
@@ -41,2 +41,17 @@ beforeEach(function() { | ||
}); | ||
it('should call clonePluginGitRepo with subdir if applicable', function() { | ||
var s = spyOn(plugins, 'clonePluginGitRepo'); | ||
var url = "https://github.com/bobeast/GAPlugin.git"; | ||
var dir = 'fakeSubDir'; | ||
fetch(url, temp, false, dir); | ||
expect(s).toHaveBeenCalledWith(url, temp, dir, undefined, undefined); | ||
}); | ||
it('should call clonePluginGitRepo with subdir and git ref if applicable', function() { | ||
var s = spyOn(plugins, 'clonePluginGitRepo'); | ||
var url = "https://github.com/bobeast/GAPlugin.git"; | ||
var dir = 'fakeSubDir'; | ||
var ref = 'fakeGitRef'; | ||
fetch(url, temp, false, dir, ref); | ||
expect(s).toHaveBeenCalledWith(url, temp, dir, ref, undefined); | ||
}); | ||
it('should throw if used with url and `link` param', function() { | ||
@@ -43,0 +58,0 @@ expect(function() { |
var install = require('../src/install'), | ||
android = require('../src/platforms/android'), | ||
common = require('../src/platforms/common'), | ||
actions = require('../src/util/action-stack'), | ||
//ios = require('../src/platforms/ios'), | ||
@@ -14,3 +14,9 @@ //blackberry = require('../src/platforms/blackberry'), | ||
childbrowser = path.join(__dirname, 'plugins', 'ChildBrowser'), | ||
dep_a = path.join(__dirname, 'plugins', 'dependencies', 'A'), | ||
dep_b = path.join(__dirname, 'plugins', 'dependencies', 'B'), | ||
dep_c = path.join(__dirname, 'plugins', 'dependencies', 'C'), | ||
dep_d = path.join(__dirname, 'plugins', 'dependencies', 'D'), | ||
dep_e = path.join(__dirname, 'plugins', 'dependencies', 'E'), | ||
dummyplugin = path.join(__dirname, 'plugins', 'DummyPlugin'), | ||
dummy_id = 'com.phonegap.plugins.dummyplugin', | ||
variableplugin = path.join(__dirname, 'plugins', 'VariablePlugin'), | ||
@@ -28,3 +34,2 @@ faultyplugin = path.join(__dirname, 'plugins', 'FaultyPlugin'), | ||
shell.mkdir('-p', temp); | ||
shell.mkdir('-p', plugins_dir); | ||
shell.cp('-rf', android_one_project, temp); | ||
@@ -36,15 +41,9 @@ }); | ||
describe('success', function() { | ||
var android_installer; | ||
beforeEach(function() { | ||
shell.cp('-rf', dummyplugin, plugins_dir); | ||
android_installer = spyOn(android, 'install'); | ||
}); | ||
it('should properly install assets', function() { | ||
var s = spyOn(common, 'copyFile').andCallThrough(); | ||
install('android', temp, 'DummyPlugin', plugins_dir, {}); | ||
install('android', temp, dummyplugin, plugins_dir, '.', {}); | ||
// making sure the right methods were called | ||
expect(s).toHaveBeenCalled(); | ||
expect(s.calls.length).toEqual(2); | ||
expect(s.calls.length).toEqual(3); | ||
@@ -63,10 +62,12 @@ expect(fs.existsSync(path.join(temp, 'assets', 'www', 'dummyplugin.js'))).toBe(true); | ||
// messing the plugin | ||
shell.rm('-rf', path.join(plugins_dir, 'dummyplugin', 'www', 'dummyplugin')); | ||
// messing with the plugin | ||
shell.mkdir('-p', plugins_dir); | ||
shell.cp('-rf', dummyplugin, plugins_dir); | ||
shell.rm('-rf', path.join(plugins_dir, 'DummyPlugin', 'www', 'dummyplugin')); | ||
expect(function() { | ||
install('android', temp, 'DummyPlugin', plugins_dir, {}); | ||
install('android', temp, 'DummyPlugin', plugins_dir, '.', {}); | ||
}).toThrow(); | ||
// making sure the right methods were called | ||
expect(sCopyFile).toHaveBeenCalled(); | ||
expect(sCopyFile.calls.length).toEqual(2); | ||
expect(sCopyFile.calls.length).toEqual(3); | ||
@@ -84,6 +85,6 @@ expect(sRemoveFile).toHaveBeenCalled(); | ||
var s = spyOn(common, 'copyFile').andCallThrough(); | ||
install('android', temp, 'DummyPlugin', plugins_dir, {}, path.join(temp, 'staging')); | ||
install('android', temp, dummyplugin, plugins_dir, '.', {}, path.join(temp, 'staging')); | ||
// making sure the right methods were called | ||
expect(s).toHaveBeenCalled(); | ||
expect(s.calls.length).toEqual(2); | ||
expect(s.calls.length).toEqual(3); | ||
@@ -104,9 +105,11 @@ expect(fs.existsSync(path.join(temp, 'staging', 'dummyplugin.js'))).toBe(true); | ||
// messing the plugin | ||
shell.mkdir('-p', plugins_dir); | ||
shell.cp('-rf', dummyplugin, plugins_dir); | ||
shell.rm('-rf', path.join(plugins_dir, 'dummyplugin', 'www', 'dummyplugin')); | ||
expect(function() { | ||
install('android', temp, 'DummyPlugin', plugins_dir, {}, path.join(temp, 'staging')); | ||
install('android', temp, 'DummyPlugin', plugins_dir, '.', {}, path.join(temp, 'staging')); | ||
}).toThrow(); | ||
// making sure the right methods were called | ||
expect(sCopyFile).toHaveBeenCalled(); | ||
expect(sCopyFile.calls.length).toEqual(2); | ||
expect(sCopyFile.calls.length).toEqual(3); | ||
@@ -124,4 +127,3 @@ expect(sRemoveFile).toHaveBeenCalled(); | ||
var s = spyOn(plugman, 'prepare'); | ||
install('android', temp, 'DummyPlugin', plugins_dir, {}); | ||
android_installer.mostRecentCall.args[5](); // fake the installer calling back successfully | ||
install('android', temp, dummyplugin, plugins_dir, '.', {}); | ||
expect(s).toHaveBeenCalled(); | ||
@@ -132,18 +134,38 @@ }); | ||
var s = spyOn(plugman, 'fetch'); | ||
install('android', temp, 'CLEANYOURSHORTS', plugins_dir, {}); | ||
install('android', temp, 'CLEANYOURSHORTS', plugins_dir, '.', {}); | ||
expect(s).toHaveBeenCalled(); | ||
}); | ||
it('should generate an array of transactions required to run an installation and pass into appropriate platform handler\'s install method', function() { | ||
install('android', temp, 'DummyPlugin', plugins_dir, {}); | ||
var transactions = android_installer.mostRecentCall.args[0]; | ||
expect(transactions.length).toEqual(1); | ||
expect(transactions[0].tag).toBe('source-file'); | ||
}); | ||
it('should call the config-changes module\'s add_installed_plugin_to_prepare_queue method', function() { | ||
install('android', temp, 'DummyPlugin', plugins_dir, {}); | ||
var spy = spyOn(config_changes, 'add_installed_plugin_to_prepare_queue'); | ||
android_installer.mostRecentCall.args[5](null); // fake out handler install callback | ||
expect(spy).toHaveBeenCalledWith(plugins_dir, 'DummyPlugin', 'android', {}); | ||
install('android', temp, dummyplugin, plugins_dir, '.', {}); | ||
expect(spy).toHaveBeenCalledWith(plugins_dir, dummy_id, 'android', {}, true); | ||
}); | ||
it('should notify if plugin is already installed into project', function() { | ||
expect(function() { | ||
install('android', temp, dummyplugin, plugins_dir,'.', {}); | ||
}).not.toThrow(); | ||
var spy = spyOn(console, 'log'); | ||
install('android', temp, dummyplugin, plugins_dir, '.', {}); | ||
expect(spy).toHaveBeenCalledWith('Plugin "com.phonegap.plugins.dummyplugin" already installed, \'sall good.'); | ||
}); | ||
describe('with dependencies', function() { | ||
it('should process all dependent plugins', function() { | ||
var spy = spyOn(actions.prototype, 'process').andCallThrough(); | ||
shell.mkdir('-p', plugins_dir); | ||
shell.cp('-rf', dep_a, plugins_dir); | ||
shell.cp('-rf', dep_d, plugins_dir); | ||
shell.cp('-rf', dep_c, plugins_dir); | ||
install('android', temp, 'A', plugins_dir, '.', {}); | ||
expect(spy.calls.length).toEqual(3); | ||
}); | ||
it('should fetch any dependent plugins if missing', function() { | ||
var spy = spyOn(plugman, 'fetch'); | ||
shell.mkdir('-p', plugins_dir); | ||
shell.cp('-rf', dep_a, plugins_dir); | ||
shell.cp('-rf', dep_c, plugins_dir); | ||
install('android', temp, 'A', plugins_dir, '.', {}); | ||
expect(spy).toHaveBeenCalled(); | ||
}); | ||
}); | ||
}); | ||
@@ -153,7 +175,6 @@ | ||
it('should throw if asset target already exists', function() { | ||
shell.cp('-rf', dummyplugin, plugins_dir); | ||
var target = path.join(temp, 'assets', 'www', 'dummyplugin.js'); | ||
fs.writeFileSync(target, 'some bs', 'utf-8'); | ||
expect(function() { | ||
install('android', temp, 'DummyPlugin', plugins_dir, {}); | ||
install('android', temp, dummyplugin, plugins_dir, '.', {}); | ||
}).toThrow(); | ||
@@ -167,42 +188,26 @@ }); | ||
it('should throw if variables are missing', function() { | ||
shell.cp('-rf', variableplugin, plugins_dir); | ||
expect(function() { | ||
install('android', temp, 'VariablePlugin', plugins_dir, {}); | ||
install('android', temp, variableplugin, plugins_dir, '.', {}); | ||
}).toThrow('Variable(s) missing: API_KEY'); | ||
}); | ||
it('should handle a failed install by passing completed transactions into appropriate handler\'s uninstall method', function() { | ||
shell.cp('-rf', faultyplugin, plugins_dir); | ||
var s = spyOn(android, 'uninstall'); | ||
install('android', temp, 'FaultyPlugin', plugins_dir, {}); | ||
var executed_txs = s.mostRecentCall.args[0]; | ||
expect(executed_txs.length).toEqual(0); | ||
}); | ||
it('should throw if plugin is already installed into project', function() { | ||
// TODO: plugins and their version can be recognized using the platform.json file | ||
it('should throw if a file required for installation cannot be found', function() { | ||
shell.mkdir('-p', plugins_dir); | ||
shell.cp('-rf', dummyplugin, plugins_dir); | ||
shell.rm(path.join(plugins_dir, 'DummyPlugin', 'src', 'android', 'DummyPlugin.java')); | ||
expect(function() { | ||
install('android', temp, 'DummyPlugin', plugins_dir, {}); | ||
}).not.toThrow(); | ||
expect(function() { | ||
install('android', temp, 'DummyPlugin', plugins_dir, {}); | ||
install('android', temp, 'DummyPlugin', plugins_dir, '.', {}); | ||
}).toThrow(); | ||
}); | ||
it('should revert web assets if an install error occurs', function() { | ||
var sRemoveFile = spyOn(common, 'removeFile').andCallThrough(); | ||
var sRemoveFileF = spyOn(common, 'removeFileF').andCallThrough(); | ||
it('should pass error into specified callback if a file required for installation cannot be found', function(done) { | ||
shell.mkdir('-p', plugins_dir); | ||
shell.cp('-rf', dummyplugin, plugins_dir); | ||
shell.rm(path.join(plugins_dir, 'DummyPlugin', 'src', 'android', 'DummyPlugin.java')); | ||
install('android', temp, 'DummyPlugin', plugins_dir, {}, undefined, function() {}); | ||
expect(sRemoveFile).toHaveBeenCalled(); | ||
expect(sRemoveFile.calls.length).toEqual(2); | ||
expect(sRemoveFileF).toHaveBeenCalled(); | ||
expect(sRemoveFileF.calls.length).toEqual(1); | ||
expect(fs.existsSync(path.join(temp, 'assets', 'www', 'dummyplugin.js'))).toBe(false); | ||
expect(fs.existsSync(path.join(temp, 'assets', 'www', 'dummyplugin'))).toBe(false); | ||
install('android', temp, 'DummyPlugin', plugins_dir, '.', {}, null, function(err) { | ||
expect(err).toBeDefined(); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); |
@@ -50,11 +50,12 @@ var android = require('../../src/platforms/android'), | ||
describe('android project handler', function() { | ||
it('should have an install function', function() { | ||
expect(typeof android.install).toEqual('function'); | ||
describe('www_dir method', function() { | ||
it('should return cordova-android project www location using www_dir', function() { | ||
expect(android.www_dir('/')).toEqual('/assets/www'); | ||
}); | ||
}); | ||
it('should have an uninstall function', function() { | ||
expect(typeof android.uninstall).toEqual('function'); | ||
describe('package_name method', function() { | ||
it('should return an android project\'s proper package name', function() { | ||
expect(android.package_name(path.join(android_one_project, '..'))).toEqual('com.alunny.childapp'); | ||
}); | ||
}); | ||
it('should return cordova-android project www location using www_dir', function() { | ||
expect(android.www_dir('/')).toEqual('/assets/www'); | ||
}); | ||
@@ -76,3 +77,3 @@ describe('installation', function() { | ||
var s = spyOn(common, 'copyFile'); | ||
android.install(source, dummy_id, temp, dummyplugin, {}); | ||
android['source-file'].install(source[0], dummyplugin, temp); | ||
expect(s).toHaveBeenCalledWith(dummyplugin, 'src/android/DummyPlugin.java', temp, 'src/com/phonegap/plugins/dummyplugin/DummyPlugin.java'); | ||
@@ -83,3 +84,3 @@ }); | ||
expect(function() { | ||
android.install(source, faulty_id, temp, faultyplugin, {}); | ||
android['source-file'].install(source[0], faultyplugin, temp); | ||
}).toThrow('"' + path.resolve(faultyplugin, 'src/android/NotHere.java') + '" not found!'); | ||
@@ -96,3 +97,3 @@ }); | ||
expect(function() { | ||
android.install(source, dummy_id, temp, dummyplugin, {}); | ||
android['source-file'].install(source[0], dummyplugin, temp); | ||
}).toThrow('"' + target + '" already exists!'); | ||
@@ -108,3 +109,2 @@ }); | ||
shell.cp('-rf', android_two_project, temp); | ||
shell.cp('-rf', dummyplugin, plugins_dir); | ||
}); | ||
@@ -117,5 +117,5 @@ afterEach(function() { | ||
var s = spyOn(common, 'deleteJava'); | ||
install('android', temp, 'DummyPlugin', plugins_dir, {}, undefined, function() { | ||
install('android', temp, dummyplugin, plugins_dir, '.', {}, undefined, function() { | ||
var source = copyArray(valid_source); | ||
android.uninstall(source, dummy_id, temp, path.join(plugins_dir, 'DummyPlugin')); | ||
android['source-file'].uninstall(source[0], temp); | ||
expect(s).toHaveBeenCalledWith(temp, 'src/com/phonegap/plugins/dummyplugin/DummyPlugin.java'); | ||
@@ -122,0 +122,0 @@ done(); |
@@ -40,11 +40,12 @@ var blackberry = require('../../src/platforms/blackberry'), | ||
describe('blackberry project handler', function() { | ||
it('should have an install function', function() { | ||
expect(typeof blackberry.install).toEqual('function'); | ||
describe('www_dir method', function() { | ||
it('should return cordova-blackberry project www location using www_dir', function() { | ||
expect(blackberry.www_dir('/')).toEqual('/www'); | ||
}); | ||
}); | ||
it('should have an uninstall function', function() { | ||
expect(typeof blackberry.uninstall).toEqual('function'); | ||
describe('package_name method', function() { | ||
it('should return the blackberry project package name based on what is in config.xml', function() { | ||
expect(blackberry.package_name(path.join(blackberry_project, '..'))).toEqual('cordovaExample'); | ||
}); | ||
}); | ||
it('should return cordova-blackberry project www location using www_dir', function() { | ||
expect(blackberry.www_dir('/')).toEqual('/www'); | ||
}); | ||
@@ -63,3 +64,5 @@ describe('installation', function() { | ||
var s = spyOn(common, 'copyFile'); | ||
blackberry.install(source, dummy_id, temp, dummyplugin, {}); | ||
source.forEach(function(src) { | ||
blackberry['source-file'].install(src, dummyplugin, temp); | ||
}); | ||
expect(s).toHaveBeenCalledWith(dummyplugin, 'src/blackberry/client.js', temp, 'ext-qnx/cordova.echo/client.js'); | ||
@@ -72,3 +75,3 @@ expect(s).toHaveBeenCalledWith(dummyplugin, 'src/blackberry/index.js', temp, 'ext-qnx/cordova.echo/index.js'); | ||
expect(function() { | ||
blackberry.install(source, faulty_id, temp, faultyplugin, {}); | ||
blackberry['source-file'].install(source[1], faultyplugin, temp); | ||
}).toThrow('"' + path.resolve(faultyplugin, 'src/blackberry/device/echoJnext.so') + '" not found!'); | ||
@@ -85,3 +88,3 @@ }); | ||
expect(function() { | ||
blackberry.install(source, dummy_id, temp, dummyplugin, {}); | ||
blackberry['source-file'].install(source[0], dummyplugin, temp); | ||
}).toThrow('"' + target + '" already exists!'); | ||
@@ -105,5 +108,7 @@ }); | ||
var s = spyOn(common, 'deleteJava'); | ||
install('blackberry', temp, 'DummyPlugin', plugins_dir, {}, undefined, function() { | ||
install('blackberry', temp, 'DummyPlugin', plugins_dir, '.', {}, undefined, function() { | ||
var source = copyArray(valid_source); | ||
blackberry.uninstall(source, dummy_id, temp, path.join(plugins_dir, 'DummyPlugin')); | ||
source.forEach(function(src) { | ||
blackberry['source-file'].uninstall(src, temp); | ||
}); | ||
expect(s).toHaveBeenCalledWith(temp, 'ext-qnx/cordova.echo/client.js'); | ||
@@ -110,0 +115,0 @@ expect(s).toHaveBeenCalledWith(temp, 'ext-qnx/cordova.echo/index.js'); |
@@ -16,2 +16,3 @@ var ios = require('../../src/platforms/ios'), | ||
ios_plist_project = path.join(__dirname, '..', 'projects', 'ios-plist', '*'), | ||
ios_project = path.join(ios_config_xml_project, '..'), | ||
xml_helpers = require('../../src/util/xml-helpers'), | ||
@@ -65,2 +66,7 @@ variableplugin = path.join(__dirname, '..', 'plugins', 'VariablePlugin'), | ||
shell.mkdir('-p', temp); | ||
shell.cp('-rf', ios_config_xml_project, temp); | ||
var proj_files = ios.parseIOSProjectFiles(temp); | ||
shell.rm('-rf', temp); | ||
function copyArray(arr) { | ||
@@ -79,16 +85,18 @@ return Array.prototype.slice.call(arr, 0); | ||
it('should have an install function', function() { | ||
expect(typeof ios.install).toEqual('function'); | ||
describe('www_dir method', function() { | ||
it('should return cordova-ios project www location using www_dir', function() { | ||
expect(ios.www_dir('/')).toEqual('/www'); | ||
}); | ||
}); | ||
it('should have an uninstall function', function() { | ||
expect(typeof ios.uninstall).toEqual('function'); | ||
describe('package_name method', function() { | ||
it('should return the CFBundleIdentifier from the project\'s Info.plist file', function() { | ||
expect(ios.package_name(ios_project)).toEqual('com.example.friendstring'); | ||
}); | ||
}); | ||
it('should return cordova-ios project www location using www_dir', function() { | ||
expect(ios.www_dir('/')).toEqual('/www'); | ||
}); | ||
describe('installation', function() { | ||
describe('parseIOSProjectFiles method', function() { | ||
it('should throw if project is not an xcode project', function() { | ||
expect(function() { | ||
ios.install([], 'someid', temp, plugins_dir, {}); | ||
ios.parseIOSProjectFiles(temp); | ||
}).toThrow('does not appear to be an xcode project (no xcode project file)'); | ||
@@ -101,6 +109,8 @@ }); | ||
expect(function() { | ||
ios.install([], 'someid', temp, plugins_dir, {}); | ||
ios.parseIOSProjectFiles(temp); | ||
}).toThrow('could not find PhoneGap/Cordova plist file, or config.xml file.'); | ||
}); | ||
}); | ||
describe('installation', function() { | ||
describe('of <source-file> elements', function() { | ||
@@ -114,3 +124,3 @@ beforeEach(function() { | ||
expect(function() { | ||
ios.install(source, faulty_id, temp, faultyplugin, {}); | ||
ios['source-file'].install(source[1], faultyplugin, temp, proj_files); | ||
}).toThrow('cannot find "' + path.resolve(faultyplugin, 'src/ios/FaultyPluginCommand.m') + '" ios <source-file>'); | ||
@@ -124,3 +134,3 @@ }); | ||
expect(function() { | ||
ios.install(source, dummy_id, temp, dummyplugin, {}); | ||
ios['source-file'].install(source[0], dummyplugin, temp, proj_files); | ||
}).toThrow('target destination "' + target + '" already exists'); | ||
@@ -130,9 +140,4 @@ }); | ||
var source = copyArray(valid_source).filter(function(s) { return s.attrib['target-dir'] == undefined}); | ||
var spy = jasmine.createSpy(); | ||
spyOn(xcode, 'project').andReturn({ | ||
parseSync:function(){}, | ||
writeSync:function(){}, | ||
addSourceFile:spy | ||
}); | ||
ios.install(source, dummy_id, temp, dummyplugin, {}); | ||
var spy = spyOn(proj_files.xcode, 'addSourceFile'); | ||
ios['source-file'].install(source[0], dummyplugin, temp, proj_files); | ||
expect(spy).toHaveBeenCalledWith(path.join('Plugins', 'DummyPluginCommand.m')); | ||
@@ -142,9 +147,4 @@ }); | ||
var source = copyArray(valid_source).filter(function(s) { return s.attrib['target-dir'] != undefined}); | ||
var spy = jasmine.createSpy(); | ||
spyOn(xcode, 'project').andReturn({ | ||
parseSync:function(){}, | ||
writeSync:function(){}, | ||
addSourceFile:spy | ||
}); | ||
ios.install(source, dummy_id, temp, dummyplugin, {}); | ||
var spy = spyOn(proj_files.xcode, 'addSourceFile'); | ||
ios['source-file'].install(source[0], dummyplugin, temp, proj_files); | ||
expect(spy).toHaveBeenCalledWith(path.join('Plugins', 'targetDir', 'TargetDirTest.m')); | ||
@@ -154,9 +154,4 @@ }); | ||
var source = copyArray(valid_source).filter(function(s) { return s.attrib['target-dir'] == undefined}); | ||
spyOn(xcode, 'project').andReturn({ | ||
parseSync:function(){}, | ||
writeSync:function(){}, | ||
addSourceFile:function() {} | ||
}); | ||
var spy = spyOn(shell, 'cp'); | ||
ios.install(source, dummy_id, temp, dummyplugin, {}); | ||
ios['source-file'].install(source[0], dummyplugin, temp, proj_files); | ||
expect(spy).toHaveBeenCalledWith(path.join(dummyplugin, 'src', 'ios', 'DummyPluginCommand.m'), path.join(temp, 'SampleApp', 'Plugins', 'DummyPluginCommand.m')); | ||
@@ -166,9 +161,4 @@ }); | ||
var source = copyArray(valid_source).filter(function(s) { return s.attrib['target-dir'] != undefined}); | ||
spyOn(xcode, 'project').andReturn({ | ||
parseSync:function(){}, | ||
writeSync:function(){}, | ||
addSourceFile:function() {} | ||
}); | ||
var spy = spyOn(shell, 'cp'); | ||
ios.install(source, dummy_id, temp, dummyplugin, {}); | ||
ios['source-file'].install(source[0], dummyplugin, temp, proj_files); | ||
expect(spy).toHaveBeenCalledWith(path.join(dummyplugin, 'src', 'ios', 'TargetDirTest.m'), path.join(temp, 'SampleApp', 'Plugins', 'targetDir', 'TargetDirTest.m')); | ||
@@ -186,3 +176,3 @@ }); | ||
expect(function() { | ||
ios.install(headers, faulty_id, temp, faultyplugin, {}); | ||
ios['header-file'].install(headers[1], faultyplugin, temp, proj_files); | ||
}).toThrow('cannot find "' + path.resolve(faultyplugin, 'src/ios/FaultyPluginCommand.h') + '" ios <header-file>'); | ||
@@ -196,3 +186,3 @@ }); | ||
expect(function() { | ||
ios.install(headers, dummy_id, temp, dummyplugin, {}); | ||
ios['header-file'].install(headers[0], dummyplugin, temp, proj_files); | ||
}).toThrow('target destination "' + target + '" already exists'); | ||
@@ -202,9 +192,4 @@ }); | ||
var headers = copyArray(valid_headers).filter(function(s) { return s.attrib['target-dir'] == undefined}); | ||
var spy = jasmine.createSpy(); | ||
spyOn(xcode, 'project').andReturn({ | ||
parseSync:function(){}, | ||
writeSync:function(){}, | ||
addHeaderFile:spy | ||
}); | ||
ios.install(headers, dummy_id, temp, dummyplugin, {}); | ||
var spy = spyOn(proj_files.xcode, 'addHeaderFile'); | ||
ios['header-file'].install(headers[0], dummyplugin, temp, proj_files); | ||
expect(spy).toHaveBeenCalledWith(path.join('Plugins', 'DummyPluginCommand.h')); | ||
@@ -214,9 +199,4 @@ }); | ||
var headers = copyArray(valid_headers).filter(function(s) { return s.attrib['target-dir'] != undefined}); | ||
var spy = jasmine.createSpy(); | ||
spyOn(xcode, 'project').andReturn({ | ||
parseSync:function(){}, | ||
writeSync:function(){}, | ||
addHeaderFile:spy | ||
}); | ||
ios.install(headers, dummy_id, temp, dummyplugin, {}); | ||
var spy = spyOn(proj_files.xcode, 'addHeaderFile'); | ||
ios['header-file'].install(headers[0], dummyplugin, temp, proj_files); | ||
expect(spy).toHaveBeenCalledWith(path.join('Plugins', 'targetDir', 'TargetDirTest.h')); | ||
@@ -226,9 +206,4 @@ }); | ||
var headers = copyArray(valid_headers).filter(function(s) { return s.attrib['target-dir'] == undefined}); | ||
spyOn(xcode, 'project').andReturn({ | ||
parseSync:function(){}, | ||
writeSync:function(){}, | ||
addHeaderFile:function() {} | ||
}); | ||
var spy = spyOn(shell, 'cp'); | ||
ios.install(headers, dummy_id, temp, dummyplugin, {}); | ||
ios['header-file'].install(headers[0], dummyplugin, temp, proj_files); | ||
expect(spy).toHaveBeenCalledWith(path.join(dummyplugin, 'src', 'ios', 'DummyPluginCommand.h'), path.join(temp, 'SampleApp', 'Plugins', 'DummyPluginCommand.h')); | ||
@@ -238,9 +213,4 @@ }); | ||
var headers = copyArray(valid_headers).filter(function(s) { return s.attrib['target-dir'] != undefined}); | ||
spyOn(xcode, 'project').andReturn({ | ||
parseSync:function(){}, | ||
writeSync:function(){}, | ||
addHeaderFile:function() {} | ||
}); | ||
var spy = spyOn(shell, 'cp'); | ||
ios.install(headers, dummy_id, temp, dummyplugin, {}); | ||
ios['header-file'].install(headers[0], dummyplugin, temp, proj_files); | ||
expect(spy).toHaveBeenCalledWith(path.join(dummyplugin, 'src', 'ios', 'TargetDirTest.h'), path.join(temp, 'SampleApp', 'Plugins', 'targetDir', 'TargetDirTest.h')); | ||
@@ -257,3 +227,3 @@ }); | ||
expect(function() { | ||
ios.install(resources, faulty_id, temp, faultyplugin, {}); | ||
ios['resource-file'].install(resources[0], faultyplugin, temp, proj_files); | ||
}).toThrow('cannot find "' + path.resolve(faultyplugin, 'src/ios/IDontExist.bundle') + '" ios <resource-file>'); | ||
@@ -267,3 +237,3 @@ }); | ||
expect(function() { | ||
ios.install(resources, dummy_id, temp, dummyplugin, {}); | ||
ios['resource-file'].install(resources[0], dummyplugin, temp, proj_files); | ||
}).toThrow('target destination "' + target + '" already exists'); | ||
@@ -273,9 +243,4 @@ }); | ||
var resources = copyArray(valid_resources); | ||
var spy = jasmine.createSpy(); | ||
spyOn(xcode, 'project').andReturn({ | ||
parseSync:function(){}, | ||
writeSync:function(){}, | ||
addResourceFile:spy | ||
}); | ||
ios.install(resources, dummy_id, temp, dummyplugin, {}); | ||
var spy = spyOn(proj_files.xcode, 'addResourceFile'); | ||
ios['resource-file'].install(resources[0], dummyplugin, temp, proj_files); | ||
expect(spy).toHaveBeenCalledWith(path.join('Resources', 'DummyPlugin.bundle')); | ||
@@ -285,9 +250,4 @@ }); | ||
var resources = copyArray(valid_resources); | ||
spyOn(xcode, 'project').andReturn({ | ||
parseSync:function(){}, | ||
writeSync:function(){}, | ||
addResourceFile:function() {} | ||
}); | ||
var spy = spyOn(shell, 'cp'); | ||
ios.install(resources, dummy_id, temp, dummyplugin, {}); | ||
ios['resource-file'].install(resources[0], dummyplugin, temp, proj_files); | ||
expect(spy).toHaveBeenCalledWith('-R', path.join(dummyplugin, 'src', 'ios', 'DummyPlugin.bundle'), path.join(temp, 'SampleApp', 'Resources')); | ||
@@ -303,9 +263,4 @@ }); | ||
var frameworks = copyArray(valid_frameworks).filter(function(f) { return f.attrib.weak == undefined; }); | ||
var spy = jasmine.createSpy(); | ||
spyOn(xcode, 'project').andReturn({ | ||
parseSync:function(){}, | ||
writeSync:function(){}, | ||
addFramework:spy | ||
}); | ||
ios.install(frameworks, dummy_id, temp, dummyplugin, {}); | ||
var spy = spyOn(proj_files.xcode, 'addFramework'); | ||
ios.framework.install(frameworks[0], dummyplugin, temp, proj_files); | ||
expect(spy).toHaveBeenCalledWith(path.join('src', 'ios', 'libsqlite3.dylib'), {weak:false}); | ||
@@ -315,9 +270,4 @@ }); | ||
var frameworks = copyArray(valid_frameworks).filter(function(f) { return f.attrib.weak != undefined; });; | ||
var spy = jasmine.createSpy(); | ||
spyOn(xcode, 'project').andReturn({ | ||
parseSync:function(){}, | ||
writeSync:function(){}, | ||
addFramework:spy | ||
}); | ||
ios.install(frameworks, dummy_id, temp, dummyplugin, {}); | ||
var spy = spyOn(proj_files.xcode, 'addFramework'); | ||
ios.framework.install(frameworks[0], dummyplugin, temp, proj_files); | ||
expect(spy).toHaveBeenCalledWith(path.join('src', 'ios', 'libsqlite3.dylib'), {weak:true}); | ||
@@ -329,15 +279,2 @@ }); | ||
describe('uninstallation', function() { | ||
it('should throw if project is not an xcode project', function() { | ||
expect(function() { | ||
ios.uninstall([], 'someid', temp, plugins_dir); | ||
}).toThrow('does not appear to be an xcode project (no xcode project file)'); | ||
}); | ||
it('should throw if project does not contain an appropriate PhoneGap/Cordova.plist file or config.xml file', function() { | ||
shell.cp('-rf', ios_config_xml_project, temp); | ||
shell.rm(path.join(temp, 'SampleApp', 'config.xml')); | ||
expect(function() { | ||
ios.uninstall([], 'someid', temp, plugins_dir); | ||
}).toThrow('could not find PhoneGap/Cordova plist file, or config.xml file.'); | ||
}); | ||
describe('of <source-file> elements', function() { | ||
@@ -347,10 +284,4 @@ it('should call into xcodeproj\'s removeSourceFile appropriately when element has no target-dir', function(){ | ||
shell.cp('-rf', ios_config_xml_project, temp); | ||
var spy = jasmine.createSpy(); | ||
spyOn(xcode, 'project').andReturn({ | ||
parseSync:function(){}, | ||
writeSync:function(){}, | ||
removeSourceFile:spy | ||
}); | ||
ios.uninstall(source, dummy_id, temp, dummyplugin); | ||
var spy = spyOn(proj_files.xcode, 'removeSourceFile'); | ||
ios['source-file'].uninstall(source[0], temp, proj_files); | ||
expect(spy).toHaveBeenCalledWith(path.join('Plugins', 'DummyPluginCommand.m')); | ||
@@ -363,3 +294,3 @@ }); | ||
var spy = spyOn(shell, 'rm'); | ||
ios.uninstall(source, dummy_id, temp, dummyplugin); | ||
ios['source-file'].uninstall(source[0], temp, proj_files); | ||
expect(spy).toHaveBeenCalledWith('-rf', path.join(temp, 'SampleApp', 'Plugins', 'targetDir', 'TargetDirTest.m')); | ||
@@ -372,3 +303,3 @@ }); | ||
var spy = spyOn(shell, 'rm'); | ||
ios.uninstall(source, dummy_id, temp, dummyplugin); | ||
ios['source-file'].uninstall(source[0], temp, proj_files); | ||
expect(spy).toHaveBeenCalledWith('-rf', path.join(temp, 'SampleApp', 'Plugins', 'DummyPluginCommand.m')); | ||
@@ -381,3 +312,3 @@ }); | ||
ios.uninstall(source, dummy_id, temp, dummyplugin); | ||
ios['source-file'].uninstall(source[0], temp, proj_files); | ||
expect(spy).toHaveBeenCalledWith('-rf', path.join(temp, 'SampleApp', 'Plugins', 'targetDir', 'TargetDirTest.m')); | ||
@@ -393,10 +324,5 @@ }); | ||
var headers = copyArray(valid_headers).filter(function(s) { return s.attrib['target-dir'] == undefined}); | ||
var spy = jasmine.createSpy(); | ||
spyOn(xcode, 'project').andReturn({ | ||
parseSync:function(){}, | ||
writeSync:function(){}, | ||
removeHeaderFile:spy | ||
}); | ||
var spy = spyOn(proj_files.xcode, 'removeHeaderFile'); | ||
ios.uninstall(headers, dummy_id, temp, dummyplugin); | ||
ios['header-file'].uninstall(headers[0], temp, proj_files); | ||
expect(spy).toHaveBeenCalledWith(path.join('Plugins', 'DummyPluginCommand.h')); | ||
@@ -406,10 +332,6 @@ }); | ||
var headers = copyArray(valid_headers).filter(function(s) { return s.attrib['target-dir'] != undefined}); | ||
var spy = jasmine.createSpy(); | ||
spyOn(xcode, 'project').andReturn({ | ||
parseSync:function(){}, | ||
writeSync:function(){}, | ||
removeHeaderFile:spy | ||
}); | ||
ios.uninstall(headers, dummy_id, temp, dummyplugin); | ||
var spy = spyOn(proj_files.xcode, 'removeHeaderFile'); | ||
ios['header-file'].uninstall(headers[0], temp, proj_files); | ||
expect(spy).toHaveBeenCalledWith(path.join('Plugins', 'targetDir', 'TargetDirTest.h')); | ||
@@ -421,3 +343,3 @@ }); | ||
ios.uninstall(headers, dummy_id, temp, dummyplugin); | ||
ios['header-file'].uninstall(headers[0], temp, proj_files); | ||
expect(spy).toHaveBeenCalledWith('-rf', path.join(temp, 'SampleApp', 'Plugins', 'targetDir', 'TargetDirTest.h')); | ||
@@ -433,10 +355,5 @@ }); | ||
var resources = copyArray(valid_resources); | ||
var spy = jasmine.createSpy(); | ||
spyOn(xcode, 'project').andReturn({ | ||
parseSync:function(){}, | ||
writeSync:function(){}, | ||
removeResourceFile:spy | ||
}); | ||
var spy = spyOn(proj_files.xcode, 'removeResourceFile'); | ||
ios.uninstall(resources, dummy_id, temp, dummyplugin); | ||
ios['resource-file'].uninstall(resources[0], temp, proj_files); | ||
expect(spy).toHaveBeenCalledWith(path.join('Resources', 'DummyPlugin.bundle')); | ||
@@ -448,3 +365,3 @@ }); | ||
ios.uninstall(resources, dummy_id, temp, dummyplugin); | ||
ios['resource-file'].uninstall(resources[0], temp, proj_files); | ||
expect(spy).toHaveBeenCalledWith('-rf', path.join(temp, 'SampleApp', 'Resources', 'DummyPlugin.bundle')); | ||
@@ -460,10 +377,5 @@ }); | ||
var frameworks = copyArray(valid_frameworks).filter(function(f) { return f.attrib.weak == undefined; }); | ||
var spy = jasmine.createSpy(); | ||
spyOn(xcode, 'project').andReturn({ | ||
parseSync:function(){}, | ||
writeSync:function(){}, | ||
removeFramework:spy | ||
}); | ||
var spy = spyOn(proj_files.xcode, 'removeFramework'); | ||
ios.uninstall(frameworks, dummy_id, temp, dummyplugin); | ||
ios.framework.uninstall(frameworks[0], temp, proj_files); | ||
expect(spy).toHaveBeenCalledWith(path.join('src', 'ios', 'libsqlite3.dylib')); | ||
@@ -470,0 +382,0 @@ }); |
@@ -1,2 +0,3 @@ | ||
var prepare = require('../src/prepare'), | ||
var platforms = require('../src/platforms'), | ||
prepare = require('../src/prepare'), | ||
fs = require('fs'), | ||
@@ -18,4 +19,4 @@ os = require('osenv'), | ||
shell.mkdir('-p', plugins_dir); | ||
shell.cp('-rf', childbrowser, plugins_dir); | ||
shell.cp('-rf', android_one_project, temp); | ||
shell.cp('-rf', childbrowser, plugins_dir); | ||
}); | ||
@@ -32,8 +33,4 @@ afterEach(function() { | ||
}); | ||
// it('should copy over assets defined in <asset> elements', function() { | ||
// prepare(temp, 'android', plugins_dir); | ||
// expect(fs.existsSync(path.join(www, 'childbrowser_file.html'))).toBe(true); | ||
// expect(fs.statSync(path.join(www, 'childbrowser')).isDirectory()).toBe(true); | ||
// }); | ||
it('should create a plugins directory in an application\'s www directory', function() { | ||
shell.cp('-rf', dummyplugin, plugins_dir); | ||
prepare(temp, 'android', plugins_dir); | ||
@@ -40,0 +37,0 @@ expect(fs.existsSync(path.join(www, 'plugins'))).toBe(true); |
var uninstall = require('../src/uninstall'), | ||
install = require('../src/install'), | ||
common = require('../src/platforms/common'), | ||
actions = require('../src/util/action-stack'), | ||
android = require('../src/platforms/android'), | ||
@@ -17,4 +18,10 @@ ios = require('../src/platforms/ios'), | ||
dummyplugin = path.join(__dirname, 'plugins', 'DummyPlugin'), | ||
dummy_id = 'com.phonegap.plugins.dummyplugin', | ||
faultyplugin = path.join(__dirname, 'plugins', 'FaultyPlugin'), | ||
childbrowserplugin = path.join(__dirname, 'plugins', 'ChildBrowser'), | ||
dep_a = path.join(__dirname, 'plugins', 'dependencies', 'A'), | ||
dep_b = path.join(__dirname, 'plugins', 'dependencies', 'B'), | ||
dep_c = path.join(__dirname, 'plugins', 'dependencies', 'C'), | ||
dep_d = path.join(__dirname, 'plugins', 'dependencies', 'D'), | ||
dep_e = path.join(__dirname, 'plugins', 'dependencies', 'E'), | ||
android_one_project = path.join(__dirname, 'projects', 'android_one', '*'), | ||
@@ -26,8 +33,7 @@ ios_project = path.join(__dirname, 'projects', 'ios-config-xml', '*'), | ||
var copied_plugin_path = path.join(temp,'ChildBrowser'); | ||
var dummy_plugin_path = path.join(plugins_dir, dummy_id); | ||
beforeEach(function() { | ||
shell.mkdir('-p', temp); | ||
shell.mkdir('-p', plugins_dir); | ||
shell.cp('-rf', android_one_project, temp); | ||
shell.cp('-rf', dummyplugin, plugins_dir); | ||
}); | ||
@@ -39,6 +45,4 @@ afterEach(function() { | ||
describe('success', function() { | ||
var android_uninstaller; | ||
beforeEach(function() { | ||
install('android', temp, 'DummyPlugin', plugins_dir, {}); | ||
android_uninstaller = spyOn(android, 'uninstall'); | ||
install('android', temp, dummyplugin, plugins_dir, '.', {}); | ||
}); | ||
@@ -49,3 +53,3 @@ it('should properly uninstall assets', function() { | ||
// making sure the right methods were called | ||
uninstall('android', temp, 'DummyPlugin', plugins_dir, {}); | ||
uninstall('android', temp, dummy_id, plugins_dir, {}); | ||
expect(s).toHaveBeenCalled(); | ||
@@ -55,3 +59,3 @@ expect(s.calls.length).toEqual(2); | ||
expect(s2).toHaveBeenCalled(); | ||
expect(s2.calls.length).toEqual(1); | ||
expect(s2.calls.length).toEqual(2); | ||
@@ -69,3 +73,3 @@ expect(fs.existsSync(path.join(temp, 'assets', 'www', 'dummyplugin.js'))).toBe(false); | ||
expect(function() { | ||
uninstall('android', temp, 'DummyPlugin', plugins_dir, {}); | ||
uninstall('android', temp, dummy_id, plugins_dir, {}); | ||
}).toThrow(); | ||
@@ -76,19 +80,37 @@ | ||
expect(sCopyFile).toHaveBeenCalled(); | ||
expect(sCopyFile.calls.length).toEqual(1); | ||
expect(sCopyFile.calls.length).toEqual(2); | ||
expect(fs.existsSync(path.join(temp, 'assets', 'www', 'dummyplugin.js'))).toBe(true); | ||
}); | ||
it('should generate and pass uninstall transaction log to appropriate platform handler\'s uninstall', function() { | ||
uninstall('android', temp, 'DummyPlugin', plugins_dir, {}); | ||
var transactions = android_uninstaller.mostRecentCall.args[0]; | ||
expect(transactions.length).toEqual(1); | ||
expect(transactions[0].tag).toBe('source-file'); | ||
}); | ||
it('should call the config-changes module\'s add_uninstalled_plugin_to_prepare_queue method', function() { | ||
uninstall('android', temp, 'DummyPlugin', plugins_dir, {}); | ||
var spy = spyOn(config_changes, 'add_uninstalled_plugin_to_prepare_queue'); | ||
android_uninstaller.mostRecentCall.args[4](null); // fake out handler uninstall callback | ||
expect(spy).toHaveBeenCalledWith(plugins_dir, 'DummyPlugin', 'android'); | ||
uninstall('android', temp, dummy_id, plugins_dir, {}); | ||
expect(spy).toHaveBeenCalledWith(plugins_dir, dummy_id, 'android', true); | ||
}); | ||
describe('with dependencies', function() { | ||
it('should uninstall any dependent plugins', function() { | ||
shell.mkdir('-p', plugins_dir); | ||
shell.cp('-rf', dep_a, plugins_dir); | ||
shell.cp('-rf', dep_d, plugins_dir); | ||
shell.cp('-rf', dep_c, plugins_dir); | ||
install('android', temp, 'A', plugins_dir, '.', {}); | ||
var spy = spyOn(actions.prototype, 'process').andCallThrough(); | ||
uninstall('android', temp, 'A', plugins_dir, {}); | ||
expect(spy.calls.length).toEqual(3); | ||
}); | ||
it('should not uninstall any dependent plugins that are required by other top-level plugins', function() { | ||
shell.mkdir('-p', plugins_dir); | ||
shell.cp('-rf', dep_a, plugins_dir); | ||
shell.cp('-rf', dep_b, plugins_dir); | ||
shell.cp('-rf', dep_d, plugins_dir); | ||
shell.cp('-rf', dep_c, plugins_dir); | ||
shell.cp('-rf', dep_e, plugins_dir); | ||
install('android', temp, 'A', plugins_dir, '.', {}); | ||
install('android', temp, 'B', plugins_dir, '.', {}); | ||
var spy = spyOn(actions.prototype, 'process').andCallThrough(); | ||
uninstall('android', temp, 'A', plugins_dir, {}); | ||
expect(spy.calls.length).toEqual(2); | ||
}); | ||
}); | ||
}); | ||
@@ -107,35 +129,3 @@ | ||
}); | ||
// it('should handle a failed uninstall by passing completed transactions into appropriate handler\'s install method', function() { | ||
// shell.rm('-rf', path.join(temp, '*')); | ||
// shell.mkdir('-p', plugins_dir); | ||
// | ||
// shell.cp('-rf', ios_project, temp); | ||
// shell.cp('-rf', childbrowserplugin, plugins_dir); | ||
// install('ios', temp, 'ChildBrowser', plugins_dir, {}); | ||
// // make uninstall fail by removing a js asset | ||
// shell.rm(path.join(temp, 'SampleApp', 'Plugins', 'ChildBrowserCommand.m')); | ||
// var s = spyOn(ios, 'install'); | ||
// uninstall('ios', temp, 'ChildBrowser', plugins_dir, {}); | ||
// var executed_txs = s.mostRecentCall.args[0]; | ||
// expect(executed_txs.length).toEqual(1); | ||
// // It only ended up "uninstalling" one source file, so install reversion should pass in that source file to re-install | ||
// expect(executed_txs[0].tag).toEqual('source-file'); | ||
// }); | ||
it('should revert assets when uninstall fails', function() { | ||
install('android', temp, 'DummyPlugin', plugins_dir, {}); | ||
var s = spyOn(common, 'copyFile').andCallThrough(); | ||
shell.rm('-rf', path.join(temp, 'src', 'com', 'phonegap', 'plugins', 'dummyplugin')); | ||
expect(function() { | ||
uninstall('android', temp, 'DummyPlugin', plugins_dir, {}); | ||
}).toThrow(); | ||
expect(s).toHaveBeenCalled(); | ||
expect(s.calls.length).toEqual(2); | ||
expect(fs.existsSync(path.join(temp, 'assets', 'www', 'dummyplugin.js'))).toBe(true); | ||
expect(fs.existsSync(path.join(temp, 'assets', 'www', 'dummyplugin'))).toBe(true); | ||
}); | ||
}); | ||
}); |
@@ -298,3 +298,3 @@ var configChanges = require('../../src/util/config-changes'), | ||
shell.cp('-rf', varplugin, plugins_dir); | ||
configChanges.add_installed_plugin_to_prepare_queue(plugins_dir, 'VariablePlugin', 'android', {"API_KEY":"hi"}); | ||
configChanges.add_installed_plugin_to_prepare_queue(plugins_dir, 'VariablePlugin', 'android', {"API_KEY":"hi"}, true); | ||
@@ -301,0 +301,0 @@ configChanges.process(plugins_dir, temp, 'android'); |
@@ -26,20 +26,38 @@ #!/usr/bin/env node | ||
shell = require('shelljs'), | ||
plugins = require('../../src/util/plugins'); | ||
plugins = require('../../src/util/plugins'), | ||
xml_helpers = require('../../src/util/xml-helpers'); | ||
describe('plugins', function(){ | ||
describe('server', function(){ | ||
it('should be able to receive the correct git clone arguments', function(){ | ||
var mySpy = spyOn(plugins, 'clonePluginGitRepo'); | ||
describe('plugins utility module', function(){ | ||
describe('clonePluginGitRepo', function(){ | ||
it('should shell out to git clone with correct arguments', function(){ | ||
var mySpy = spyOn(shell, 'exec'); | ||
var plugin_git_url = 'https://github.com/imhotep/ChildBrowser' | ||
var myFunction = function(){}; | ||
plugins.clonePluginGitRepo(plugin_git_url, temp, myFunction); | ||
plugins.clonePluginGitRepo(plugin_git_url, temp, '.'); | ||
expect(mySpy).toHaveBeenCalled(); | ||
expect(mySpy).toHaveBeenCalledWith(plugin_git_url, temp, myFunction); | ||
expect(mySpy).toHaveBeenCalled(); | ||
var git_clone_regex = new RegExp('^git clone "' + plugin_git_url + '" ".*"$', 'gi'); | ||
expect(mySpy.mostRecentCall.args[0]).toMatch(git_clone_regex); | ||
}); | ||
it('should take into account subdirectory argument when copying over final repository into plugins+plugin_id directory', function() { | ||
var exec_spy = spyOn(shell, 'exec'); | ||
var cp_spy = spyOn(shell, 'cp'); | ||
var fake_id = 'VillageDrunkard'; | ||
var xml_spy = spyOn(xml_helpers, 'parseElementtreeSync').andReturn({ | ||
getroot:function() { | ||
return { | ||
attrib:{id:fake_id} | ||
}; | ||
} | ||
}); | ||
var plugin_git_url = 'https://github.com/imhotep/ChildBrowser' | ||
var fake_subdir = 'TheBrainRecoilsInHorror'; | ||
plugins.clonePluginGitRepo(plugin_git_url, temp, fake_subdir); | ||
exec_spy.mostRecentCall.args[2](0); // fake out shell.exec finishing appropriately (so we dont ACTUALLY shell out to git-clone, assume it worked fine) | ||
var expected_subdir_cp_path = new RegExp(fake_subdir + '[\\\\\\/]\\*$', 'gi'); | ||
expect(cp_spy.mostRecentCall.args[1]).toMatch(expected_subdir_cp_path); | ||
expect(cp_spy.mostRecentCall.args[2]).toEqual(path.join(temp, fake_id)); | ||
}); | ||
}); | ||
}); | ||
var shell = require('shelljs'), | ||
fs = require('fs'), | ||
plugins = require('./util/plugins'), | ||
xml_helpers = require('./util/xml-helpers'), | ||
path = require('path'); | ||
module.exports = function fetchPlugin(plugin_dir, plugins_dir, link, callback) { | ||
module.exports = function fetchPlugin(plugin_dir, plugins_dir, link, subdir, git_ref, callback) { | ||
// Ensure the containing directory exists. | ||
shell.mkdir('-p', plugins_dir); | ||
subdir = subdir || '.'; | ||
// clone from git repository | ||
@@ -17,13 +20,19 @@ if(plugin_dir.indexOf('https://') == 0 || plugin_dir.indexOf('git://') == 0) { | ||
} else { | ||
plugins.clonePluginGitRepo(plugin_dir, plugins_dir, callback); | ||
plugins.clonePluginGitRepo(plugin_dir, plugins_dir, subdir, git_ref, callback); | ||
} | ||
} else { | ||
// Copy from the local filesystem. | ||
var dest = path.join(plugins_dir, path.basename(plugin_dir)); | ||
// First, read the plugin.xml and grab the ID. | ||
plugin_dir = path.join(plugin_dir, subdir); | ||
var xml = xml_helpers.parseElementtreeSync(path.join(plugin_dir, 'plugin.xml')); | ||
var plugin_id = xml.getroot().attrib.id; | ||
var dest = path.join(plugins_dir, plugin_id); | ||
shell.rm('-rf', dest); | ||
if (link) { | ||
fs.symlinkSync(path.resolve(plugin_dir), dest, 'dir'); | ||
fs.symlinkSync(plugin_dir, dest, 'dir'); | ||
} else { | ||
shell.cp('-R', plugin_dir, plugins_dir); // Yes, not dest. | ||
shell.mkdir('-p', dest); | ||
shell.cp('-R', path.join(plugin_dir, '*') , dest); | ||
} | ||
@@ -30,0 +39,0 @@ |
var path = require('path'), | ||
fs = require('fs'), | ||
et = require('elementtree'), | ||
config_changes = require('./util/config-changes'); | ||
n = require('ncallbacks'), | ||
config_changes = require('./util/config-changes'), | ||
action_stack = require('./util/action-stack'), | ||
platform_modules = require('./platforms'); | ||
// TODO: is name necessary as a param ehre? | ||
module.exports = function installPlugin(platform, project_dir, name, plugins_dir, cli_variables, www_dir, callback) { | ||
module.exports = function installPlugin(platform, project_dir, id, plugins_dir, subdir, cli_variables, www_dir, callback) { | ||
if (!platform_modules[platform]) { | ||
var err = new Error(platform + " not supported."); | ||
if (callback) { | ||
callback(err); | ||
return; | ||
} | ||
if (callback) callback(err); | ||
else throw err; | ||
return; | ||
} | ||
var plugin_dir = path.join(plugins_dir, name); | ||
var current_stack = new action_stack(); | ||
possiblyFetch(current_stack, platform, project_dir, id, plugins_dir, subdir, cli_variables, www_dir, true, callback); | ||
}; | ||
function possiblyFetch(actions, platform, project_dir, id, plugins_dir, subdir, cli_variables, www_dir, is_top_level, callback) { | ||
var plugin_dir = path.join(plugins_dir, id); | ||
// Check that the plugin has already been fetched. | ||
if (!fs.existsSync(plugin_dir)) { | ||
// if plugin doesnt exist, use fetch to get it. | ||
require('../plugman').fetch(name, plugins_dir, false, function(err, plugin_dir) { | ||
// TODO: Actual value for git_ref. | ||
require('../plugman').fetch(id, plugins_dir, false, '.', undefined /* git_ref */, function(err, plugin_dir) { | ||
if (err) { | ||
@@ -28,11 +34,11 @@ callback(err); | ||
// update ref to plugin_dir after successful fetch, via fetch callback | ||
runInstall(platform, project_dir, plugin_dir, plugins_dir, cli_variables, www_dir, callback); | ||
runInstall(actions, platform, project_dir, plugin_dir, plugins_dir, cli_variables, www_dir, is_top_level, callback); | ||
} | ||
}); | ||
} else { | ||
runInstall(platform, project_dir, plugin_dir, plugins_dir, cli_variables, www_dir, callback); | ||
runInstall(actions, platform, project_dir, plugin_dir, plugins_dir, cli_variables, www_dir, is_top_level, callback); | ||
} | ||
}; | ||
} | ||
function runInstall(platform, project_dir, plugin_dir, plugins_dir, cli_variables, www_dir, callback) { | ||
function runInstall(actions, platform, project_dir, plugin_dir, plugins_dir, cli_variables, www_dir, is_top_level, callback) { | ||
var xml_path = path.join(plugin_dir, 'plugin.xml') | ||
@@ -44,2 +50,22 @@ , xml_text = fs.readFileSync(xml_path, 'utf-8') | ||
var plugin_id = plugin_et._root.attrib['id']; | ||
// check if platform has plugin installed already. | ||
var platform_config = config_changes.get_platform_json(plugins_dir, platform); | ||
var plugin_basename = path.basename(plugin_dir); | ||
var is_installed = false; | ||
Object.keys(platform_config.installed_plugins).forEach(function(installed_plugin_id) { | ||
if (installed_plugin_id == plugin_id) { | ||
is_installed = true; | ||
} | ||
}); | ||
Object.keys(platform_config.dependent_plugins).forEach(function(installed_plugin_id) { | ||
if (installed_plugin_id == plugin_id) { | ||
is_installed = true; | ||
} | ||
}); | ||
if (is_installed) { | ||
console.log('Plugin "' + plugin_id + '" already installed, \'sall good.'); | ||
if (callback) callback(); | ||
return; | ||
} | ||
@@ -64,107 +90,75 @@ // checking preferences, if certain variables are not provided, we should throw. | ||
// check if platform has plugin fully installed or queued already. | ||
var platform_config = config_changes.get_platform_json(plugins_dir, platform); | ||
var plugin_basename = path.basename(plugin_dir); | ||
if (platform_config.prepare_queue.installed.indexOf(plugin_basename) > -1) { | ||
var err = new Error('plugin "' + plugin_basename + '" is already installed (but needs to be prepared)'); | ||
if (callback) callback(err); | ||
else throw err; | ||
return; | ||
// Check for dependencies, (co)recurse to install each one | ||
var dependencies = plugin_et.findall('dependency'); | ||
if (dependencies && dependencies.length) { | ||
var end = n(dependencies.length, function() { | ||
handleInstall(actions, plugin_id, plugin_et, platform, project_dir, plugins_dir, plugin_basename, plugin_dir, filtered_variables, www_dir, is_top_level, callback); | ||
}); | ||
dependencies.forEach(function(dep) { | ||
var dep_plugin_id = dep.attrib.id; | ||
var dep_subdir = dep.attrib.subdir; | ||
var dep_url = dep.attrib.url; | ||
if (dep_subdir) { | ||
dep_subdir = path.join.apply(null, dep_subdir.split('/')); | ||
} | ||
var dep_plugin_dir = path.join(plugins_dir, dep_plugin_id); | ||
if (fs.existsSync(dep_plugin_dir)) { | ||
console.log('Dependent plugin ' + dep_plugin_id + ' already fetched, using that version.'); | ||
runInstall(actions, platform, project_dir, dep_plugin_dir, plugins_dir, filtered_variables, www_dir, false, end); | ||
} else { | ||
console.log('Dependent plugin ' + dep_plugin_id + ' not fetched, retrieving then installing.'); | ||
possiblyFetch(actions, platform, project_dir, dep_url, plugins_dir, dep_subdir, filtered_variables, www_dir, false, end); | ||
} | ||
}); | ||
} else { | ||
handleInstall(actions, plugin_id, plugin_et, platform, project_dir, plugins_dir, plugin_basename, plugin_dir, filtered_variables, www_dir, is_top_level, callback); | ||
} | ||
var is_fully_installed = false; | ||
Object.keys(platform_config.installed_plugins).forEach(function(installed_plugin_id) { | ||
if (installed_plugin_id == plugin_id) { | ||
is_fully_installed = true; | ||
} | ||
}); | ||
if (is_fully_installed) { | ||
var err = new Error('plugin "' + plugin_basename + '" (id: '+plugin_id+') is already installed'); | ||
if (callback) callback(err); | ||
else throw err; | ||
return; | ||
} | ||
// TODO: if plugin does not have platform tag but has platform-agnostic config changes, should we queue it up? | ||
var platformTag = plugin_et.find('./platform[@name="'+platform+'"]'); | ||
if (!platformTag) { | ||
// Either this plugin doesn't support this platform, or it's a JS-only plugin. | ||
// Either way, return now. | ||
// should call prepare probably! | ||
finalizeInstall(project_dir, plugins_dir, platform, plugin_basename, filtered_variables, callback); | ||
return; | ||
} | ||
} | ||
// parse plugin.xml into transactions | ||
function handleInstall(actions, plugin_id, plugin_et, platform, project_dir, plugins_dir, plugin_basename, plugin_dir, filtered_variables, www_dir, is_top_level, callback) { | ||
var handler = platform_modules[platform]; | ||
var txs = []; | ||
var sourceFiles = platformTag.findall('./source-file'), | ||
headerFiles = platformTag.findall('./header-file'), | ||
resourceFiles = platformTag.findall('./resource-file'), | ||
assets = platformTag.findall('./asset'), | ||
frameworks = platformTag.findall('./framework'); | ||
www_dir = www_dir || handler.www_dir(project_dir); | ||
assets = assets.concat(plugin_et.findall('./asset')); | ||
var platformTag = plugin_et.find('./platform[@name="'+platform+'"]'); | ||
var assets = plugin_et.findall('asset'); | ||
if (platformTag) { | ||
var sourceFiles = platformTag.findall('./source-file'), | ||
headerFiles = platformTag.findall('./header-file'), | ||
resourceFiles = platformTag.findall('./resource-file'), | ||
frameworks = platformTag.findall('./framework'); | ||
assets = assets.concat(platformTag.findall('./asset')); | ||
// asset installation | ||
var installedAssets = []; | ||
// queue up native stuff | ||
sourceFiles && sourceFiles.forEach(function(source) { | ||
actions.push(actions.createAction(handler["source-file"].install, [source, plugin_dir, project_dir], handler["source-file"].uninstall, [source, project_dir])); | ||
}); | ||
headerFiles && headerFiles.forEach(function(header) { | ||
actions.push(actions.createAction(handler["header-file"].install, [header, plugin_dir, project_dir], handler["header-file"].uninstall, [header, project_dir])); | ||
}); | ||
resourceFiles && resourceFiles.forEach(function(resource) { | ||
actions.push(actions.createAction(handler["resource-file"].install, [resource, plugin_dir, project_dir], handler["resource-file"].uninstall, [resource, project_dir])); | ||
}); | ||
frameworks && frameworks.forEach(function(framework) { | ||
actions.push(actions.createAction(handler["framework"].install, [framework, plugin_dir, project_dir], handler["framework"].uninstall, [framework, project_dir])); | ||
}); | ||
} | ||
// queue up asset installation | ||
var common = require('./platforms/common'); | ||
www_dir = www_dir || handler.www_dir(project_dir); | ||
try { | ||
for(var i = 0, j = assets.length ; i < j ; i++) { | ||
var src = assets[i].attrib['src'], | ||
target = assets[i].attrib['target']; | ||
common.copyFile(plugin_dir, src, www_dir, target); | ||
installedAssets.push(assets[i]); | ||
} | ||
} catch(err) { | ||
var issue = 'asset installation failed\n'+err.stack+'\n'; | ||
try { | ||
// removing assets and reverting install | ||
for(var i = 0, j = installedAssets.length ; i < j ; i++) { | ||
common.removeFile(www_dir, installedAssets[i].attrib.target); | ||
} | ||
common.removeFileF(path.resolve(www_dir, 'plugins', plugin_id)); | ||
issue += 'but successfully reverted\n'; | ||
} catch(err2) { | ||
issue += 'and reversion failed :(\n' + err2.stack; | ||
} | ||
var error = new Error(issue); | ||
if (callback) callback(error); | ||
else throw error; | ||
} | ||
txs = txs.concat(sourceFiles, headerFiles, resourceFiles, frameworks); | ||
// pass platform-specific transactions into install | ||
handler.install(txs, plugin_id, project_dir, plugin_dir, filtered_variables, function(err) { | ||
assets && assets.forEach(function(asset) { | ||
actions.push(actions.createAction(common.asset.install, [asset, plugin_dir, www_dir], common.asset.uninstall, [asset, www_dir, plugin_id])); | ||
}); | ||
// run through the action stack | ||
actions.process(platform, project_dir, function(err) { | ||
if (err) { | ||
// FAIL | ||
var issue = ''; | ||
try { | ||
for(var i = 0, j = installedAssets.length ; i < j ; i++) { | ||
common.removeFile(www_dir, installedAssets[i].attrib.target); | ||
} | ||
common.removeFileF(path.resolve(www_dir, 'plugins', plugin_id)); | ||
} catch(err2) { | ||
issue += 'Could not revert assets' + err2.stack + '\n'; | ||
} | ||
if (err.transactions) { | ||
handler.uninstall(err.transactions.executed, plugin_id, project_dir, plugin_dir, function(superr) { | ||
if (superr) { | ||
// Even reversion failed. super fail. | ||
issue += 'Install failed, then reversion of installation failed. Sorry :(. Instalation issue: ' + err.stack + ', reversion issue: ' + superr.stack; | ||
} else { | ||
issue += 'Install failed, plugin reversion successful so you should be good to go. Installation issue: ' + err.stack; | ||
} | ||
var error = new Error(issue); | ||
if (callback) callback(error); | ||
else throw error; | ||
}); | ||
} else { | ||
if (callback) callback(err); | ||
else throw err; | ||
} | ||
if (callback) callback(err); | ||
else throw err; | ||
} else { | ||
// WIN! | ||
// Log out plugin INFO element contents in case additional install steps are necessary | ||
var info = platformTag.findall('./info'); | ||
var info = (platformTag ? platformTag.findall('./info') : ''); | ||
if(info.length) { | ||
@@ -174,14 +168,11 @@ console.log(info[0].text); | ||
finalizeInstall(project_dir, plugins_dir, platform, plugin_basename, filtered_variables, callback); | ||
// queue up the plugin so prepare knows what to do. | ||
config_changes.add_installed_plugin_to_prepare_queue(plugins_dir, plugin_basename, platform, filtered_variables, is_top_level); | ||
// call prepare after a successful install | ||
require('./../plugman').prepare(project_dir, platform, plugins_dir); | ||
console.log(plugin_id + ' installed.'); | ||
if (callback) callback(); | ||
} | ||
}); | ||
} | ||
function finalizeInstall(project_dir, plugins_dir, platform, plugin_name, variables, callback) { | ||
// queue up the plugin so prepare knows what to do. | ||
config_changes.add_installed_plugin_to_prepare_queue(plugins_dir, plugin_name, platform, variables); | ||
// call prepare after a successful install | ||
require('./../plugman').prepare(project_dir, platform, plugins_dir); | ||
if (callback) callback(); | ||
} |
@@ -22,15 +22,6 @@ /* | ||
, path = require('path') | ||
, shell = require('shelljs') | ||
, common = require('./common') | ||
, getConfigChanges = require(path.join(__dirname, '..', 'util', 'config-changes')) | ||
, plugins_module = require(path.join(__dirname, '..', 'util', 'plugins')) | ||
, xml_helpers = require(path.join(__dirname, '..', 'util', 'xml-helpers')); | ||
module.exports = { | ||
install:function(transactions, plugin_id, project_dir, plugin_dir, variables, callback) { | ||
handlePlugin('install', plugin_id, transactions, project_dir, plugin_dir, variables, callback); | ||
}, | ||
uninstall:function(transactions, plugin_id, project_dir, plugin_dir, callback) { | ||
handlePlugin('uninstall', plugin_id, transactions, project_dir, plugin_dir, null, callback); | ||
}, | ||
www_dir:function(project_dir) { | ||
@@ -46,49 +37,13 @@ return path.join(project_dir, 'assets', 'www'); | ||
return mDoc._root.attrib['package']; | ||
}, | ||
"source-file":{ | ||
install:function(source_el, plugin_dir, project_dir) { | ||
var dest = path.join(source_el.attrib['target-dir'], path.basename(source_el.attrib['src'])); | ||
common.copyFile(plugin_dir, source_el.attrib['src'], project_dir, dest); | ||
}, | ||
uninstall:function(source_el, project_dir) { | ||
var dest = path.join(source_el.attrib['target-dir'], path.basename(source_el.attrib['src'])); | ||
common.deleteJava(project_dir, dest); | ||
} | ||
} | ||
}; | ||
function handlePlugin(action, plugin_id, txs, project_dir, plugin_dir, variables, callback) { | ||
variables = variables || {}; | ||
// TODO: adding access tags? | ||
// TODO: move this to prepare? | ||
/* | ||
var root = et.Element("config-file"); | ||
root.attrib['parent'] = '.'; | ||
plugin_et.findall('./access').forEach(function (tag) { | ||
root.append(tag); | ||
}); | ||
*/ | ||
var completed = []; | ||
while(txs.length) { | ||
var mod = txs.shift(); | ||
try { | ||
switch(mod.tag.toLowerCase()) { | ||
case 'source-file': | ||
var destFile = path.join(mod.attrib['target-dir'], path.basename(mod.attrib['src'])); | ||
if (action == 'install') { | ||
common.copyFile(plugin_dir, mod.attrib['src'], project_dir, destFile); | ||
} else { | ||
common.deleteJava(project_dir, destFile); | ||
} | ||
break; | ||
default: | ||
throw new Error('Unrecognized plugin.xml element/action in android installer: ' + mod.tag); | ||
break; | ||
} | ||
} catch(e) { | ||
// propagate error up and provide completed tx log | ||
e.transactions = { | ||
executed:completed, | ||
incomplete:txs.unshift(mod) | ||
}; | ||
if (callback) callback(e); | ||
else throw e; | ||
return; | ||
} | ||
completed.push(mod); | ||
} | ||
if (callback) callback(); | ||
} |
@@ -22,5 +22,2 @@ /* | ||
, path = require('path') | ||
, shell = require('shelljs') | ||
, et = require('elementtree') | ||
, getConfigChanges = require('../util/config-changes') | ||
, common = require('./common') | ||
@@ -30,8 +27,2 @@ , xml_helpers = require(path.join(__dirname, '..', 'util', 'xml-helpers')); | ||
module.exports = { | ||
install:function(transactions, plugin_id, project_dir, plugin_dir, variables, callback) { | ||
handlePlugin('install', plugin_id, transactions, project_dir, plugin_dir, variables, callback); | ||
}, | ||
uninstall:function(transactions, plugin_id, project_dir, plugin_dir, callback) { | ||
handlePlugin('uninstall', plugin_id, transactions, project_dir, plugin_dir, null, callback); | ||
}, | ||
www_dir:function(project_dir) { | ||
@@ -42,39 +33,15 @@ return path.join(project_dir, 'www'); | ||
var config_path = path.join(module.exports.www_dir(project_dir), 'config.xml'); | ||
var widget_doc = new et.ElementTree(et.XML(fs.readFileSync(config_path, 'utf-8'))); | ||
var widget_doc = xml_helpers.parseElementtreeSync(config_path); | ||
return widget_doc._root.attrib['id']; | ||
}, | ||
"source-file":{ | ||
install:function(source_el, plugin_dir, project_dir) { | ||
var dest = path.join(source_el.attrib['target-dir'], path.basename(source_el.attrib['src'])); | ||
common.copyFile(plugin_dir, source_el.attrib['src'], project_dir, dest); | ||
}, | ||
uninstall:function(source_el, project_dir) { | ||
var dest = path.join(source_el.attrib['target-dir'], path.basename(source_el.attrib['src'])); | ||
common.deleteJava(project_dir, dest); | ||
} | ||
} | ||
}; | ||
function handlePlugin(action, plugin_id, txs, project_dir, plugin_dir, variables, callback) { | ||
var completed = []; | ||
while(txs.length) { | ||
var mod = txs.shift(); | ||
try { | ||
switch(mod.tag.toLowerCase()) { | ||
case 'source-file': | ||
var destFile = path.join(mod.attrib['target-dir'], path.basename(mod.attrib['src'])); | ||
if (action == 'install') { | ||
common.copyFile(plugin_dir, mod.attrib['src'], project_dir, destFile); | ||
} else { | ||
common.deleteJava(project_dir, destFile); | ||
} | ||
break; | ||
default: | ||
throw new Error('Unrecognized plugin.xml element/action in blackberry installer: ' + mod.tag); | ||
break; | ||
} | ||
} catch(e) { | ||
// propagate error up and provide completed tx log | ||
e.transactions = { | ||
executed:completed, | ||
incomplete:txs.unshift(mod) | ||
}; | ||
if (callback) callback(e); | ||
else throw e; | ||
return; | ||
} | ||
completed.push(mod); | ||
} | ||
if (callback) callback(); | ||
} |
@@ -58,3 +58,16 @@ var shell = require('shelljs'), | ||
} | ||
}, | ||
// handle <asset> elements | ||
asset:{ | ||
install:function(asset_el, plugin_dir, www_dir) { | ||
var src = asset_el.attrib.src; | ||
var target = asset_el.attrib.target; | ||
module.exports.copyFile(plugin_dir, src, www_dir, target); | ||
}, | ||
uninstall:function(asset_el, www_dir, plugin_id) { | ||
var target = asset_el.attrib.target; | ||
module.exports.removeFile(www_dir, target); | ||
module.exports.removeFileF(path.resolve(www_dir, 'plugins', plugin_id)); | ||
} | ||
} | ||
}; |
@@ -23,19 +23,7 @@ /* | ||
, glob = require('glob') | ||
, et = require('elementtree') | ||
, xcode = require('xcode') | ||
, plist = require('plist') | ||
, bplist = require('bplist-parser') | ||
, shell = require('shelljs') | ||
, common = require('./common') | ||
, xml_helpers = require(path.join(__dirname, '..', 'util', 'xml-helpers')) | ||
, searchAndReplace = require(path.join(__dirname, '..', 'util', 'search-and-replace')) | ||
, getConfigChanges = require(path.join(__dirname, '..', 'util', 'config-changes')); | ||
, shell = require('shelljs'); | ||
module.exports = { | ||
install:function(transactions, plugin_id, project_dir, plugin_dir, variables, callback) { | ||
handlePlugin('install', plugin_id, transactions, project_dir, plugin_dir, variables, callback); | ||
}, | ||
uninstall:function(transactions, plugin_id, project_dir, plugin_dir, callback) { | ||
handlePlugin('uninstall', plugin_id, transactions, project_dir, plugin_dir, null, callback); | ||
}, | ||
www_dir:function(project_dir) { | ||
@@ -47,142 +35,122 @@ return path.join(project_dir, 'www'); | ||
return plist.parseFileSync(plist_file).CFBundleIdentifier; | ||
} | ||
}; | ||
function handlePlugin(action, plugin_id, txs, project_dir, plugin_dir, variables, callback) { | ||
variables = variables || {}; | ||
}, | ||
"source-file":{ | ||
install:function(source_el, plugin_dir, project_dir, project) { | ||
var src = source_el.attrib['src']; | ||
var srcFile = path.resolve(plugin_dir, src); | ||
var targetDir = path.resolve(project.plugins_dir, getRelativeDir(source_el)); | ||
var destFile = path.resolve(targetDir, path.basename(src)); | ||
// grab and parse pbxproj | ||
// we don't want CordovaLib's xcode project | ||
var project_files = glob.sync(path.join(project_dir, '*.xcodeproj', 'project.pbxproj')); | ||
if (project_files.length === 0) { | ||
var err = new Error("does not appear to be an xcode project (no xcode project file)"); | ||
if (callback) callback(err); | ||
else throw err; | ||
return; | ||
} | ||
var pbxPath = project_files[0]; | ||
var xcodeproj = xcode.project(pbxPath); | ||
xcodeproj.parseSync(); | ||
if (!fs.existsSync(srcFile)) throw new Error('cannot find "' + srcFile + '" ios <source-file>'); | ||
if (fs.existsSync(destFile)) throw new Error('target destination "' + destFile + '" already exists'); | ||
project.xcode.addSourceFile(path.join('Plugins', path.relative(project.plugins_dir, destFile))); | ||
shell.mkdir('-p', targetDir); | ||
shell.cp(srcFile, destFile); | ||
}, | ||
uninstall:function(source_el, project_dir, project) { | ||
var src = source_el.attrib['src']; | ||
var targetDir = path.resolve(project.plugins_dir, getRelativeDir(source_el)); | ||
var destFile = path.resolve(targetDir, path.basename(src)); | ||
// grab and parse plist file or config.xml | ||
var config_files = (glob.sync(path.join(project_dir, '**', '{PhoneGap,Cordova}.plist')).length == 0 ? | ||
glob.sync(path.join(project_dir, '**', 'config.xml')) : | ||
glob.sync(path.join(project_dir, '**', '{PhoneGap,Cordova}.plist')) | ||
); | ||
project.xcode.removeSourceFile(path.join('Plugins', path.relative(project.plugins_dir, destFile))); | ||
shell.rm('-rf', destFile); | ||
if(fs.existsSync(targetDir) && fs.readdirSync(targetDir).length>0){ | ||
shell.rm('-rf', targetDir); | ||
} | ||
} | ||
}, | ||
"header-file":{ | ||
install:function(header_el, plugin_dir, project_dir, project) { | ||
var src = header_el.attrib['src']; | ||
var srcFile = path.resolve(plugin_dir, src); | ||
var targetDir = path.resolve(project.plugins_dir, getRelativeDir(header_el)); | ||
var destFile = path.resolve(targetDir, path.basename(src)); | ||
if (!fs.existsSync(srcFile)) throw new Error('cannot find "' + srcFile + '" ios <header-file>'); | ||
if (fs.existsSync(destFile)) throw new Error('target destination "' + destFile + '" already exists'); | ||
project.xcode.addHeaderFile(path.join('Plugins', path.relative(project.plugins_dir, destFile))); | ||
shell.mkdir('-p', targetDir); | ||
shell.cp(srcFile, destFile); | ||
}, | ||
uninstall:function(header_el, project_dir, project) { | ||
var src = header_el.attrib['src']; | ||
var targetDir = path.resolve(project.plugins_dir, getRelativeDir(header_el)); | ||
var destFile = path.resolve(targetDir, path.basename(src)); | ||
project.xcode.removeHeaderFile(path.join('Plugins', path.relative(project.plugins_dir, destFile))); | ||
shell.rm('-rf', destFile); | ||
if(fs.existsSync(targetDir) && fs.readdirSync(targetDir).length>0){ | ||
shell.rm('-rf', targetDir); | ||
} | ||
} | ||
}, | ||
"resource-file":{ | ||
install:function(resource_el, plugin_dir, project_dir, project) { | ||
var src = resource_el.attrib['src'], | ||
srcFile = path.resolve(plugin_dir, src), | ||
destFile = path.resolve(project.resources_dir, path.basename(src)); | ||
if (!fs.existsSync(srcFile)) throw new Error('cannot find "' + srcFile + '" ios <resource-file>'); | ||
if (fs.existsSync(destFile)) throw new Error('target destination "' + destFile + '" already exists'); | ||
project.xcode.addResourceFile(path.join('Resources', path.basename(src))); | ||
shell.cp('-R', srcFile, project.resources_dir); | ||
}, | ||
uninstall:function(resource_el, project_dir, project) { | ||
var src = resource_el.attrib['src'], | ||
destFile = path.resolve(project.resources_dir, path.basename(src)); | ||
project.xcode.removeResourceFile(path.join('Resources', path.basename(src))); | ||
shell.rm('-rf', destFile); | ||
} | ||
}, | ||
"framework":{ | ||
install:function(framework_el, plugin_dir, project_dir, project) { | ||
var src = framework_el.attrib['src'], | ||
weak = framework_el.attrib['weak']; | ||
var opt = { weak: (weak == undefined || weak == null || weak != 'true' ? false : true ) }; | ||
project.xcode.addFramework(src, opt); | ||
}, | ||
uninstall:function(framework_el, project_dir, project) { | ||
var src = framework_el.attrib['src']; | ||
project.xcode.removeFramework(src); | ||
} | ||
}, | ||
parseIOSProjectFiles:function(project_dir) { | ||
// grab and parse pbxproj | ||
// we don't want CordovaLib's xcode project | ||
var project_files = glob.sync(path.join(project_dir, '*.xcodeproj', 'project.pbxproj')); | ||
if (project_files.length === 0) { | ||
throw new Error("does not appear to be an xcode project (no xcode project file)"); | ||
} | ||
var pbxPath = project_files[0]; | ||
var xcodeproj = xcode.project(pbxPath); | ||
xcodeproj.parseSync(); | ||
config_files = config_files.filter(function (val) { | ||
return !(/^build\//.test(val)); | ||
}); | ||
// grab and parse plist file or config.xml | ||
var config_files = (glob.sync(path.join(project_dir, '**', '{PhoneGap,Cordova}.plist')).length == 0 ? | ||
glob.sync(path.join(project_dir, '**', 'config.xml')) : | ||
glob.sync(path.join(project_dir, '**', '{PhoneGap,Cordova}.plist')) | ||
); | ||
if (config_files.length === 0) { | ||
var err = new Error("could not find PhoneGap/Cordova plist file, or config.xml file."); | ||
if (callback) callback(err); | ||
else throw err; | ||
return; | ||
} | ||
config_files = config_files.filter(function (val) { | ||
return !(/^build\//.test(val)); | ||
}); | ||
var config_file = config_files[0]; | ||
var config_filename = path.basename(config_file); | ||
var xcode_dir = path.dirname(config_file); | ||
var pluginsDir = path.resolve(xcode_dir, 'Plugins'); | ||
var resourcesDir = path.resolve(xcode_dir, 'Resources'); | ||
// get project plist for package name | ||
var project_plists = glob.sync(xcode_dir + '/*-Info.plist'); | ||
var projectPListPath = project_plists[0]; | ||
if (config_files.length === 0) { | ||
throw new Error("could not find PhoneGap/Cordova plist file, or config.xml file."); | ||
} | ||
// for certain config changes, we need to know if plugins-plist elements are present | ||
var plistEle = txs.filter(function(t) { return t.tag.toLowerCase() == 'plugins-plist'; })[0]; | ||
var config_file = config_files[0]; | ||
var config_filename = path.basename(config_file); | ||
var xcode_dir = path.dirname(config_file); | ||
var pluginsDir = path.resolve(xcode_dir, 'Plugins'); | ||
var resourcesDir = path.resolve(xcode_dir, 'Resources'); | ||
var completed = []; | ||
while(txs.length) { | ||
var mod = txs.shift(); | ||
try { | ||
switch(mod.tag.toLowerCase()) { | ||
case 'source-file': | ||
var src = mod.attrib['src']; | ||
var srcFile = path.resolve(plugin_dir, src); | ||
var targetDir = path.resolve(pluginsDir, getRelativeDir(mod)); | ||
var destFile = path.resolve(targetDir, path.basename(src)); | ||
if (action == 'install') { | ||
if (!fs.existsSync(srcFile)) throw new Error('cannot find "' + srcFile + '" ios <source-file>'); | ||
if (fs.existsSync(destFile)) throw new Error('target destination "' + destFile + '" already exists'); | ||
xcodeproj.addSourceFile(path.join('Plugins', path.relative(pluginsDir, destFile))); | ||
shell.mkdir('-p', targetDir); | ||
shell.cp(srcFile, destFile); | ||
} else { | ||
xcodeproj.removeSourceFile(path.join('Plugins', path.relative(pluginsDir, destFile))); | ||
shell.rm('-rf', destFile); | ||
if(fs.existsSync(targetDir) && fs.readdirSync(targetDir).length>0){ | ||
shell.rm('-rf', targetDir); | ||
} | ||
} | ||
break; | ||
case 'header-file': | ||
var src = mod.attrib['src']; | ||
var srcFile = path.resolve(plugin_dir, src); | ||
var targetDir = path.resolve(pluginsDir, getRelativeDir(mod)); | ||
var destFile = path.resolve(targetDir, path.basename(src)); | ||
if (action == 'install') { | ||
if (!fs.existsSync(srcFile)) throw new Error('cannot find "' + srcFile + '" ios <header-file>'); | ||
if (fs.existsSync(destFile)) throw new Error('target destination "' + destFile + '" already exists'); | ||
xcodeproj.addHeaderFile(path.join('Plugins', path.relative(pluginsDir, destFile))); | ||
shell.mkdir('-p', targetDir); | ||
shell.cp(srcFile, destFile); | ||
} else { | ||
// TODO: doesnt preserve-dirs affect what the originally-added path to xcodeproj (see above) affect how we should call remove too? | ||
xcodeproj.removeHeaderFile(path.join('Plugins', path.relative(pluginsDir, destFile))); | ||
shell.rm('-rf', destFile); | ||
// TODO: again.. is this right? same as source-file | ||
shell.rm('-rf', targetDir); | ||
} | ||
break; | ||
case 'resource-file': | ||
var src = mod.attrib['src'], | ||
srcFile = path.resolve(plugin_dir, src), | ||
destFile = path.resolve(resourcesDir, path.basename(src)); | ||
if (action == 'install') { | ||
if (!fs.existsSync(srcFile)) throw new Error('cannot find "' + srcFile + '" ios <resource-file>'); | ||
if (fs.existsSync(destFile)) throw new Error('target destination "' + destFile + '" already exists'); | ||
xcodeproj.addResourceFile(path.join('Resources', path.basename(src))); | ||
shell.cp('-R', srcFile, resourcesDir); | ||
} else { | ||
xcodeproj.removeResourceFile(path.join('Resources', path.basename(src))); | ||
shell.rm('-rf', destFile); | ||
} | ||
break; | ||
case 'framework': | ||
var src = mod.attrib['src'], | ||
weak = mod.attrib['weak']; | ||
if (action == 'install') { | ||
var opt = { weak: (weak == undefined || weak == null || weak != 'true' ? false : true ) }; | ||
xcodeproj.addFramework(src, opt); | ||
} else { | ||
xcodeproj.removeFramework(src); | ||
} | ||
break; | ||
default: | ||
throw new Error('Unrecognized plugin.xml element/action in ios installer: ' + mod.tag); | ||
break; | ||
} | ||
} catch(e) { | ||
// propagate error up and provide completed tx log | ||
e.transactions = { | ||
executed:completed, | ||
incomplete:txs.unshift(mod) | ||
}; | ||
if (callback) callback(e); | ||
else throw e; | ||
return; | ||
} | ||
completed.push(mod); | ||
return { | ||
plugins_dir:pluginsDir, | ||
resources_dir:resourcesDir, | ||
xcode:xcodeproj, | ||
pbx:pbxPath | ||
}; | ||
} | ||
// write out xcodeproj file | ||
fs.writeFileSync(pbxPath, xcodeproj.writeSync()); | ||
}; | ||
if (callback) callback(); | ||
} | ||
function getRelativeDir(file) { | ||
@@ -196,10 +164,1 @@ var targetDir = file.attrib['target-dir']; | ||
} | ||
// determine if a plist file is binary | ||
function isBinaryPlist(filename) { | ||
// I wish there was a synchronous way to read only the first 6 bytes of a | ||
// file. This is wasteful :/ | ||
var buf = '' + fs.readFileSync(filename, 'utf8'); | ||
// binary plists start with a magic header, "bplist" | ||
return buf.substring(0, 6) === 'bplist'; | ||
} |
@@ -61,31 +61,3 @@ /** | ||
// Copy all the <asset>s into the platform's www/ | ||
// var assets = xml.findall('./asset'); | ||
// assets && assets.forEach(function(asset) { | ||
// var target = asset.attrib.target; | ||
// | ||
// var lastSlash = target.lastIndexOf('/'); | ||
// var dirname = lastSlash < 0 ? '' : target.substring(0, lastSlash); | ||
// var basename = lastSlash < 0 ? target : target.substring(lastSlash + 1); | ||
// var targetDir = path.join(wwwDir, dirname); | ||
// | ||
// shell.mkdir('-p', targetDir); | ||
// var srcFile = path.join(pluginDir, asset.attrib.src); | ||
// var targetFile = path.join(targetDir, basename); | ||
// var cpOptions = '-f'; | ||
// | ||
// if(fs.statSync(srcFile).isDirectory()){ | ||
// shell.mkdir('-p',targetFile); | ||
// srcFile = srcFile+'/*'; | ||
// cpOptions = '-Rf'; | ||
// } | ||
// shell.cp(cpOptions, [srcFile], targetFile); | ||
// | ||
// }); | ||
// And then add the plugins dir to the platform's www. | ||
// add the plugins dir to the platform's www. | ||
var platformPluginsDir = path.join(wwwDir, 'plugins'); | ||
@@ -92,0 +64,0 @@ // XXX this should not be here if there are no js-module. It leaves an empty plugins/ directory |
var path = require('path'), | ||
fs = require('fs'), | ||
et = require('elementtree'), | ||
config_changes = require('./util/config-changes'); | ||
shell= require('shelljs'), | ||
config_changes = require('./util/config-changes'), | ||
action_stack = require('./util/action-stack'), | ||
n = require('ncallbacks'), | ||
dependencies = require('./util/dependencies'), | ||
underscore = require('underscore'), | ||
platform_modules = require('./platforms'); | ||
module.exports = function uninstallPlugin(platform, project_dir, name, plugins_dir, cli_variables, callback) { | ||
module.exports = function uninstallPlugin(platform, project_dir, id, plugins_dir, cli_variables, www_dir, callback) { | ||
if (!platform_modules[platform]) { | ||
@@ -14,7 +20,6 @@ var err = new Error(platform + " not supported."); | ||
// Check that the plugin has already been fetched. | ||
var plugin_dir = path.join(plugins_dir, name); | ||
var plugin_dir = path.join(plugins_dir, id); | ||
if (!fs.existsSync(plugin_dir)) { | ||
var err = new Error('Plugin "' + name + '" not found. Already uninstalled?'); | ||
var err = new Error('Plugin "' + id + '" not found. Already uninstalled?'); | ||
if (callback) callback(err); | ||
@@ -25,6 +30,8 @@ else throw err; | ||
runUninstall(platform, project_dir, plugin_dir, plugins_dir, cli_variables, callback); | ||
var current_stack = new action_stack(); | ||
runUninstall(current_stack, platform, project_dir, plugin_dir, plugins_dir, cli_variables, www_dir, true, callback); | ||
}; | ||
function runUninstall(platform, project_dir, plugin_dir, plugins_dir, cli_variables, callback) { | ||
function runUninstall(actions, platform, project_dir, plugin_dir, plugins_dir, cli_variables, www_dir, is_top_level, callback) { | ||
var xml_path = path.join(plugin_dir, 'plugin.xml') | ||
@@ -36,88 +43,90 @@ , xml_text = fs.readFileSync(xml_path, 'utf-8') | ||
var platformTag = plugin_et.find('./platform[@name="'+platform+'"]'); | ||
if (!platformTag) { | ||
// Either this plugin doesn't support this platform, or it's a JS-only plugin. | ||
// Either way, return now. | ||
// should call prepare probably! | ||
require('./../plugman').prepare(project_dir, platform, plugins_dir); | ||
if (callback) callback(); | ||
return; | ||
var dependency_info = dependencies.generate_dependency_info(plugins_dir, platform); | ||
var graph = dependency_info.graph; | ||
var dependents = graph.getChain(plugin_id); | ||
var tlps = dependency_info.top_level_plugins; | ||
var diff_arr = []; | ||
tlps.forEach(function(tlp) { | ||
if (tlp != plugin_id) { | ||
var ds = graph.getChain(tlp); | ||
if (is_top_level && ds.indexOf(plugin_id) > -1) { | ||
var err = new Error('Another top-level plugin (' + tlp + ') relies on plugin ' + plugin_id + ', therefore aborting uninstallation.'); | ||
if (callback) callback(err); | ||
else throw err; | ||
return; | ||
} | ||
diff_arr.push(ds); | ||
} | ||
}); | ||
// if this plugin has dependencies, do a set difference to determine which dependencies are not required by other existing plugins | ||
diff_arr.unshift(dependents); | ||
var danglers = underscore.difference.apply(null, diff_arr); | ||
if (dependents.length && danglers && danglers.length) { | ||
var end = n(danglers.length, function() { | ||
handleUninstall(actions, platform, plugin_id, plugin_et, project_dir, www_dir, plugins_dir, plugin_dir, is_top_level, callback); | ||
}); | ||
danglers.forEach(function(dangler) { | ||
var dependent_path = path.join(plugins_dir, dangler); | ||
runUninstall(actions, platform, project_dir, dependent_path, plugins_dir, cli_variables, www_dir, false /* TODO: should this "is_top_level" param be false for dependents? */, end); | ||
}); | ||
} else { | ||
// this plugin can get axed by itself, gogo! | ||
handleUninstall(actions, platform, plugin_id, plugin_et, project_dir, www_dir, plugins_dir, plugin_dir, is_top_level, callback); | ||
} | ||
} | ||
function handleUninstall(actions, platform, plugin_id, plugin_et, project_dir, www_dir, plugins_dir, plugin_dir, is_top_level, callback) { | ||
var platform_modules = require('./platforms'); | ||
// parse plugin.xml into transactions | ||
var handler = platform_modules[platform]; | ||
var txs = []; | ||
var sourceFiles = platformTag.findall('./source-file'), | ||
headerFiles = platformTag.findall('./header-file'), | ||
resourceFiles = platformTag.findall('./resource-file'), | ||
assets = platformTag.findall('./asset'), | ||
frameworks = platformTag.findall('./framework'); | ||
assets = assets.concat(plugin_et.findall('./asset')); | ||
// asset uninstallation | ||
var uninstalledAssets = []; | ||
var platformTag = plugin_et.find('./platform[@name="'+platform+'"]'); | ||
www_dir = www_dir || handler.www_dir(project_dir); | ||
var assets = plugin_et.findall('./asset'); | ||
if (platformTag) { | ||
var sourceFiles = platformTag.findall('./source-file'), | ||
headerFiles = platformTag.findall('./header-file'), | ||
resourceFiles = platformTag.findall('./resource-file'), | ||
frameworks = platformTag.findall('./framework'); | ||
assets = assets.concat(platformTag.findall('./asset')); | ||
// queue up native stuff | ||
sourceFiles && sourceFiles.forEach(function(source) { | ||
actions.push(actions.createAction(handler["source-file"].uninstall, [source, project_dir], handler["source-file"].install, [source, plugin_dir, project_dir])); | ||
}); | ||
headerFiles && headerFiles.forEach(function(header) { | ||
actions.push(actions.createAction(handler["header-file"].uninstall, [header, project_dir], handler["header-file"].install, [header, plugin_dir, project_dir])); | ||
}); | ||
resourceFiles && resourceFiles.forEach(function(resource) { | ||
actions.push(actions.createAction(handler["resource-file"].uninstall, [resource, project_dir], handler["resource-file"].install, [resource, plugin_dir, project_dir])); | ||
}); | ||
frameworks && frameworks.forEach(function(framework) { | ||
actions.push(actions.createAction(handler["framework"].uninstall, [framework, project_dir], handler["framework"].install, [framework, plugin_dir, project_dir])); | ||
}); | ||
} | ||
// queue up asset installation | ||
var common = require('./platforms/common'); | ||
try { | ||
for(var i = 0, j = assets.length ; i < j ; i++) { | ||
common.removeFile(handler.www_dir(project_dir), assets[i].attrib['target']); | ||
uninstalledAssets.push(assets[i]); | ||
} | ||
common.removeFileF(path.resolve(handler.www_dir(project_dir), 'plugins', plugin_id)); | ||
} catch(err) { | ||
var issue = 'asset uninstallation failed\n'+err.stack+'\n'; | ||
try { | ||
// adding assets back | ||
for(var i = 0, j = uninstalledAssets.length ; i < j ; i++) { | ||
var src = uninstalledAssets[i].attrib['src'], | ||
target = uninstalledAssets[i].attrib['target']; | ||
common.copyFile(plugin_dir, src, handler.www_dir(project_dir), target); | ||
} | ||
issue += 'but successfully reverted\n'; | ||
} catch(err2) { | ||
issue += 'and reversion failed :(\n' + err2.stack; | ||
} | ||
var error = new Error(issue); | ||
if (callback) callback(error); | ||
else throw error; | ||
} | ||
txs = txs.concat(sourceFiles, headerFiles, resourceFiles, frameworks); | ||
// pass platform-specific transactions into uninstall | ||
handler.uninstall(txs, plugin_id, project_dir, plugin_dir, function(err) { | ||
assets && assets.forEach(function(asset) { | ||
actions.push(actions.createAction(common.asset.uninstall, [asset, www_dir, plugin_id], common.asset.install, [asset, plugin_dir, www_dir])); | ||
}); | ||
// run through the action stack | ||
actions.process(platform, project_dir, function(err) { | ||
if (err) { | ||
// FAIL | ||
var issue = ''; | ||
try { | ||
for(var i = 0, j = uninstalledAssets.length ; i < j ; i++) { | ||
var src = uninstalledAssets[i].attrib['src'], | ||
target = uninstalledAssets[i].attrib['target']; | ||
common.copyFile(plugin_dir, src, handler.www_dir(project_dir), target); | ||
} | ||
} catch(err2) { | ||
issue += 'Could not revert assets' + err2.stack + '\n'; | ||
} | ||
if (err. transactions) { | ||
handler.install(err.transactions.executed, plugin_id, project_dir, plugin_dir, cli_variables, function(superr) { | ||
if (superr) { | ||
// Even reversion failed. super fail. | ||
issue += 'Uninstall failed, then reversion of uninstallation failed. Sorry :(. Uninstalation issue: ' + err.stack + ', reversion issue: ' + superr.stack; | ||
} else { | ||
issue += 'Uninstall failed, plugin reversion successful so you should be good to go. Uninstallation issue: ' + err.stack; | ||
} | ||
var error = new Error(issue); | ||
if (callback) callback(error); | ||
else throw error; | ||
}); | ||
} else { | ||
if (callback) callback(err); | ||
else throw err; | ||
} | ||
if (callback) callback(err); | ||
else throw err; | ||
} else { | ||
// WIN! | ||
// queue up the plugin so prepare can remove the config changes | ||
config_changes.add_uninstalled_plugin_to_prepare_queue(plugins_dir, path.basename(plugin_dir), platform); | ||
config_changes.add_uninstalled_plugin_to_prepare_queue(plugins_dir, path.basename(plugin_dir), platform, is_top_level); | ||
// call prepare after a successful uninstall | ||
require('./../plugman').prepare(project_dir, platform, plugins_dir); | ||
// axe the directory | ||
shell.rm('-rf', plugin_dir); | ||
console.log(plugin_id + ' uninstalled.'); | ||
if (callback) callback(); | ||
@@ -124,0 +133,0 @@ } |
@@ -26,3 +26,2 @@ /* | ||
et = require('elementtree'), | ||
platforms = require('./../platforms'), | ||
xml_helpers = require('./../util/xml-helpers'), | ||
@@ -32,14 +31,14 @@ plist_helpers = require('./../util/plist-helpers'); | ||
function checkPlatform(platform) { | ||
if (!(platform in platforms)) throw new Error('platform "' + platform + '" not recognized.'); | ||
if (!(platform in require('./../platforms'))) throw new Error('platform "' + platform + '" not recognized.'); | ||
} | ||
module.exports = { | ||
add_installed_plugin_to_prepare_queue:function(plugins_dir, plugin, platform, vars) { | ||
add_installed_plugin_to_prepare_queue:function(plugins_dir, plugin, platform, vars, is_top_level) { | ||
checkPlatform(platform); | ||
var config = module.exports.get_platform_json(plugins_dir, platform); | ||
config.prepare_queue.installed.push({'plugin':plugin, 'vars':vars}); | ||
config.prepare_queue.installed.push({'plugin':plugin, 'vars':vars, 'topLevel':is_top_level}); | ||
module.exports.save_platform_json(config, plugins_dir, platform); | ||
}, | ||
add_uninstalled_plugin_to_prepare_queue:function(plugins_dir, plugin, platform) { | ||
add_uninstalled_plugin_to_prepare_queue:function(plugins_dir, plugin, platform, is_top_level) { | ||
checkPlatform(platform); | ||
@@ -49,3 +48,3 @@ | ||
var config = module.exports.get_platform_json(plugins_dir, platform); | ||
config.prepare_queue.uninstalled.push({'plugin':plugin, 'id':plugin_xml._root.attrib['id']}); | ||
config.prepare_queue.uninstalled.push({'plugin':plugin, 'id':plugin_xml._root.attrib['id'], 'topLevel':is_top_level}); | ||
module.exports.save_platform_json(config, plugins_dir, platform); | ||
@@ -63,3 +62,4 @@ }, | ||
config_munge:{}, | ||
installed_plugins:{} | ||
installed_plugins:{}, | ||
dependent_plugins:{} | ||
}; | ||
@@ -80,3 +80,3 @@ fs.writeFileSync(filepath, JSON.stringify(config), 'utf-8'); | ||
vars = vars || {}; | ||
var platform_handler = platforms[platform]; | ||
var platform_handler = require('./../platforms')[platform]; | ||
// Add PACKAGE_NAME variable into vars | ||
@@ -147,3 +147,3 @@ if (!vars['PACKAGE_NAME']) { | ||
var plugin_id = u.id; | ||
var plugin_vars = platform_config.installed_plugins[plugin_id]; | ||
var plugin_vars = (u.topLevel ? platform_config.installed_plugins[plugin_id] : platform_config.dependent_plugins[plugin_id]); | ||
@@ -219,3 +219,7 @@ // get config munge, aka how did this plugin change various config files | ||
// Remove from installed_plugins | ||
delete platform_config.installed_plugins[plugin_id] | ||
if (u.topLevel) { | ||
delete platform_config.installed_plugins[plugin_id] | ||
} else { | ||
delete platform_config.dependent_plugins[plugin_id] | ||
} | ||
}); | ||
@@ -302,4 +306,8 @@ | ||
// Move to installed_plugins | ||
platform_config.installed_plugins[plugin_id] = plugin_vars || {}; | ||
// Move to installed_plugins if it is a top-level plugin | ||
if (u.topLevel) { | ||
platform_config.installed_plugins[plugin_id] = plugin_vars || {}; | ||
} else { | ||
platform_config.dependent_plugins[plugin_id] = plugin_vars || {}; | ||
} | ||
}); | ||
@@ -306,0 +314,0 @@ |
@@ -26,3 +26,5 @@ #!/usr/bin/env node | ||
util = require('util'), | ||
shell = require('shelljs'); | ||
shell = require('shelljs'), | ||
xml_helpers = require('./xml-helpers'), | ||
tmp_dir = path.join(os.tmpdir(), 'plugman-tmp'); | ||
@@ -32,3 +34,3 @@ module.exports = { | ||
// Fetches plugin information from remote server | ||
clonePluginGitRepo:function(plugin_git_url, plugins_dir, callback) { | ||
clonePluginGitRepo:function(plugin_git_url, plugins_dir, subdir, git_ref, callback) { | ||
if(!shell.which('git')) { | ||
@@ -40,21 +42,38 @@ var err = new Error('git command line is not installed'); | ||
var plugin_dir = path.join(plugins_dir, path.basename(plugin_git_url).replace(path.extname(plugin_git_url), '')); | ||
shell.rm('-rf', tmp_dir); | ||
// trash it if it already exists (something went wrong before probably) | ||
// TODO: is this the correct behaviour? | ||
if(fs.existsSync(plugin_dir)) { | ||
shell.rm('-rf', plugin_dir); | ||
} | ||
shell.exec('git clone ' + plugin_git_url + ' ' + plugin_dir, {silent: true, async:true}, function(code, output) { | ||
var cmd = util.format('git clone "%s" "%s"', plugin_git_url, tmp_dir); | ||
shell.exec(cmd, {silent: true, async:true}, function(code, output) { | ||
if (code > 0) { | ||
var err = new Error('failed to get the plugin via git from URL '+ plugin_git_url); | ||
var err = new Error('failed to get the plugin via git from URL '+ plugin_git_url + ', output: ' + output); | ||
if (callback) callback(err) | ||
else throw err; | ||
} else { | ||
// Check out the specified revision, if provided. | ||
if (git_ref) { | ||
var result = shell.exec(util.format('git checkout "%s"', git_ref), { silent: true }); | ||
if (result.code > 0) { | ||
var err = new Error('failed to checkout git ref "' + git_ref + '"'); | ||
if (callback) callback(err); | ||
else throw err; | ||
} | ||
} | ||
// Read the plugin.xml file and extract the plugin's ID. | ||
tmp_dir = path.join(tmp_dir, subdir); | ||
// TODO: what if plugin.xml does not exist? | ||
var xml_file = path.join(tmp_dir, 'plugin.xml'); | ||
var xml = xml_helpers.parseElementtreeSync(xml_file); | ||
var plugin_id = xml.getroot().attrib.id; | ||
// TODO: what if a plugin dependended on different subdirectories of the same plugin? this would fail. | ||
// should probably copy over entire plugin git repo contents into plugins_dir and handle subdir seperately during install. | ||
var plugin_dir = path.join(plugins_dir, plugin_id); | ||
shell.cp('-R', path.join(tmp_dir, '*'), plugin_dir); | ||
if (callback) callback(null, plugin_dir); | ||
} | ||
}); | ||
}, | ||
} | ||
}; | ||
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
3909092
194
498
28
11
3995
+ Addeddep-graph@1.1.0
+ Addedncallbacks@1.1.0
+ Addedunderscore@1.4.4
+ Addeddep-graph@1.1.0(transitive)
+ Addedncallbacks@1.1.0(transitive)
+ Addedunderscore@1.2.11.4.4(transitive)