..also, seems its not warning when I attempt to remove a previously saved
plugin.  Not sure if that is a bug I introduced in my manual patch or if it
existed before.

-Michal


On Wed, May 7, 2014 at 2:03 PM, Michal Mocny <mmo...@chromium.org> wrote:

> So, this patch needs updating now that cordova-lib was split out.  I've
> manually applied the patch locally to test this feature (pasted below, so
> you don't have to redo the work), but please update the exiting PR to only
> contain changes to cli.js and submit a new PR to the cordova-lib repo (
> https://github.com/apache/cordova-lib) with what I have below + whatever
> changes you want.  I didn't move your spec tests so those need re-adding.
>
> Comments:
> - I like that you can "cordova restore plugins" when platforms/ and
> plugins/ folders don't exist (so its useful from a fresh git clone of a
> cordova app).
> - Your save/restore feature is saving/restoring the full list of currently
> installed plugins and the specific versions they are at currently -- it is
> not storing the list of "cordova plugin add FOO" the user actually typed.
>  I think these are two very different things, akin perhaps to npm
> shrinkwrap.
>   - For this reason, I think we should land this very useful feature, but
> leave it experimental for a while, until we debate exactly what we want
> long term.
>
> I will add an --experimental flag to CLI, and then we can hide features
> like this behind that flag for now.  Then at least it isn't stuck in a PR.
>
> Okay?
>
> Patch to cordova-lib:
> ========
>
> diff --git a/cordova-lib/src/cordova/ConfigParser.js
> b/cordova-lib/src/cordova/ConfigParser.js
> index 262e75f..4cea08a 100644
> --- a/cordova-lib/src/cordova/ConfigParser.js
> +++ b/cordova-lib/src/cordova/ConfigParser.js
> @@ -155,6 +155,22 @@ ConfigParser.prototype = {
>
>          return ret;
>      },
> +    /**
> +     *This does not check for duplicate feature entries
> +     */
> +    addFeature: function (name, params){
> +      var el = new et.Element('feature');
> +        el.attrib.name = name;
> +        if(params){
> +          params.forEach(function(param){
> +            var p = new et.Element('param');
> +            p.attrib.name = param.name;
> +            p.attrib.value = param.value;
> +            el.append(p);
> +          });
> +        }
> +        this.doc.getroot().append(el);
> +    },
>      write:function() {
>          fs.writeFileSync(this.path, this.doc.write({indent: 4}), 'utf-8');
>      }
> diff --git a/cordova-lib/src/cordova/cordova.js
> b/cordova-lib/src/cordova/cordova.js
> index abd6709..6fcb05e 100644
> --- a/cordova-lib/src/cordova/cordova.js
> +++ b/cordova-lib/src/cordova/cordova.js
> @@ -64,5 +64,7 @@ addModuleProperty(module, 'platforms', './platform',
> true);
>  addModuleProperty(module, 'compile', './compile', true);
>  addModuleProperty(module, 'run', './run', true);
>  addModuleProperty(module, 'info', './info', true);
> +addModuleProperty(module, 'save', './save', true);
> +addModuleProperty(module, 'restore', './restore', true);
>
>
> diff --git a/cordova-lib/src/cordova/plugin.js
> b/cordova-lib/src/cordova/plugin.js
> index d488e1e..11f4686 100644
> --- a/cordova-lib/src/cordova/plugin.js
> +++ b/cordova-lib/src/cordova/plugin.js
> @@ -27,6 +27,8 @@ var cordova_util  = require('./util'),
>       Q             = require('q'),
>      CordovaError  = require('../CordovaError'),
>      PluginInfo    = require('../PluginInfo'),
>  +    ConfigParser  = require('./ConfigParser'),
> +    fs            = require('fs'),
>      events        = require('./events');
>
>  // Returns a promise.
> @@ -173,6 +175,15 @@ module.exports = function plugin(command, targets,
> opts) {
>                              var platformRoot = path.join(projectRoot,
> 'platforms', platform);
>                              var platforms = require('./platforms');
>                              var parser = new
> platforms[platform].parser(platformRoot);
> +                            //check if plugin is restorable and warn
> +                            var configPath =
> cordova_util.projectConfig(projectRoot);
> +                            if(fs.existsSync(configPath)){//should not
> happen with real life but needed for tests
> +                                var configXml = new
> ConfigParser(configPath);
> +                                var features =
> configXml.doc.findall('./feature/param[@name="id"][@value="'+target+'"]/..');
> +                                if(features && features.length){
> +                                    events.emit('results','"'+target + '"
> plugin is restorable, call "cordova save plugins" to remove it from
> restorable plugins list');
> +                                }
> +                            }
>                              events.emit('verbose', 'Calling
> plugman.uninstall on plugin "' + target + '" for platform "' + platform +
> '"');
>                              return
> plugman.raw.uninstall.uninstallPlatform(platform, platformRoot, target,
> path.join(projectRoot, 'plugins'));
>                          });
> diff --git a/cordova-lib/src/cordova/restore.js
> b/cordova-lib/src/cordova/restore.js
> new file mode 100644
> index 0000000..6414e00
> --- /dev/null
> +++ b/cordova-lib/src/cordova/restore.js
> @@ -0,0 +1,76 @@
> +/**
> +    Licensed to the Apache Software Foundation (ASF) under one
> +    or more contributor license agreements.  See the NOTICE file
> +    distributed with this work for additional information
> +    regarding copyright ownership.  The ASF licenses this file
> +    to you under the Apache License, Version 2.0 (the
> +    "License"); you may not use this file except in compliance
> +    with the License.  You may obtain a copy of the License at
> +
> +    http://www.apache.org/licenses/LICENSE-2.0
> +
> +    Unless required by applicable law or agreed to in writing,
> +    software distributed under the License is distributed on an
> +    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> +    KIND, either express or implied.  See the License for the
> +    specific language governing permissions and limitations
> +    under the License.
> +*/
> +
> +var cordova_util    = require('./util'),
> +    ConfigParser     = require('./ConfigParser'),
> +    path             = require('path'),
> +    xml              = require('../util/xml-helpers')
> +    Q                = require('q'),
> +    fs               = require('fs'),
> +    plugin           = require('./plugin'),
> +    events           = require('./events');
> +
> +module.exports = function restore(target){
> +    var projectHome = cordova_util.cdProjectRoot();
> +    var configPath = cordova_util.projectConfig(projectHome);
> +    var configXml = new ConfigParser(configPath);
> +    return installPluginsFromConfigXML(configXml);
> +}
> +
> +
> +//returns a Promise
> +function installPluginsFromConfigXML(cfg){
> +        //Install plugins that are listed on config.xml
> +        var pluginsFromConfig = new Array();
> +        var projectRoot = cordova_util.cdProjectRoot();
> +        var plugins_dir = path.join(projectRoot, 'plugins');
> +
> +        var features = cfg.doc.findall('feature');
> +        features.forEach(function(feature){
> +          var params = feature.findall('param');
> +          var pluginId = "";
> +          var pluginVersion = "";
> +          for( var i =0; i < params.length; i++){
> +            if(params[i].attrib.name === 'id'){
> +              pluginId = params[i].attrib.value;
> +            }
> +            if(params[i].attrib.name === 'version'){
> +              pluginVersion = params[i].attrib.value;
> +            }
> +          }
> +          var pluginPath =  path.join(plugins_dir,pluginId);
> +          // contents of the plugins folder takes precedence hence
> +          // we ignore if the correct version is installed or not.
> +          if(pluginId !== "" && !fs.existsSync(pluginPath)){
> +            if( pluginVersion !== ""){
> +              pluginId = pluginId +"@"+pluginVersion;
> +            }
> +            events.emit('log', "Discovered "+ pluginId + " in config.xml.
> Installing to the project")
> +            pluginsFromConfig.push(pluginId);
> +          }
> +
> +        })
> +
> +        //Use cli instead of plugman directly ensuring all the hooks
> +        // to get fired.
> +        if(pluginsFromConfig.length >0){
> +            return plugin("add",pluginsFromConfig);
> +        }
> +        return Q.all("No config.xml plugins to install");
> +}
> diff --git a/cordova-lib/src/cordova/save.js
> b/cordova-lib/src/cordova/save.js
> new file mode 100644
> index 0000000..b767caa
> --- /dev/null
> +++ b/cordova-lib/src/cordova/save.js
> @@ -0,0 +1,79 @@
> +/**
> +    Licensed to the Apache Software Foundation (ASF) under one
> +    or more contributor license agreements.  See the NOTICE file
> +    distributed with this work for additional information
> +    regarding copyright ownership.  The ASF licenses this file
> +    to you under the Apache License, Version 2.0 (the
> +    "License"); you may not use this file except in compliance
> +    with the License.  You may obtain a copy of the License at
> +
> +    http://www.apache.org/licenses/LICENSE-2.0
> +
> +    Unless required by applicable law or agreed to in writing,
> +    software distributed under the License is distributed on an
> +    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> +    KIND, either express or implied.  See the License for the
> +    specific language governing permissions and limitations
> +    under the License.
> +*/
> +
> +var cordova_util    = require('./util'),
> +    ConfigParser     = require('./ConfigParser'),
> +    path             = require('path'),
> +    xml              = require('../util/xml-helpers')
> +    Q                = require('q'),
> +    events           = require('./events');
> +
> +module.exports = function save(target){
> +  var projectHome = cordova_util.cdProjectRoot();
> +  var configPath = cordova_util.projectConfig(projectHome);
> +  var configXml = new ConfigParser(configPath);
> +  var pluginsPath = path.join(projectHome, 'plugins');
> +  var plugins = cordova_util.findPlugins(pluginsPath);
> +  var features = configXml.doc.findall('./feature/param[@name="id"]/..');
> +  //clear obsolete features with id params.
> +  for(var i=0; i<features.length; i++){
> +     //somehow elementtree remove fails on me
>  +     var childs = configXml.doc.getroot().getchildren();
> +     var idx = childs.indexOf(features[i]);
> +     if(idx > -1){
> +        childs.splice(idx,1);
> +      }
> +  }
> +  // persist the removed features here if there are no plugins
> +  // to be added to config.xml otherwise we can delay the
> +  // persist to add feature
> +  if((!plugins || plugins.length<1) &&
> +        (features && features.length)){
> +      configXml.write();
> +  }
> +
> +  return Q.all(plugins.map(function(plugin){
> +    var currentPluginPath = path.join(pluginsPath,plugin);
> +    var name = readPluginName(currentPluginPath);
> +    var id = plugin;
> +    var version = readPluginVersion(currentPluginPath);
> +    var params = [{name:"id", value:id},{name:"version", value: version}];
> +    configXml.addFeature(name,params);
> +    configXml.write();
> +    events.emit('results', 'Saved plugin info for "'+plugin+'" to
> config.xml');
> +    return Q();
> +  }));
> +}
> +
> +function readPluginName(pluginPath){
> +    var xml_path = path.join(pluginPath, 'plugin.xml');
> +    var et = xml.parseElementtreeSync(xml_path);
> +    var el = et.getroot().find('name');
> +    if(el && el.text){
> +       return el.text.trim();
> +    }
> +    return "";
> +}
> +
> +function readPluginVersion(pluginPath){
> +    var xml_path = path.join(pluginPath, 'plugin.xml');
> +    var et = xml.parseElementtreeSync(xml_path);
> +    var version = et.getroot().attrib.version;
> +    return version;
> +}
>
>
>
>  On Tue, May 6, 2014 at 11:20 AM, Gorkem Ercan <gorkem.er...@gmail.com>wrote:
>
>> Any updates for me?
>> --
>> Gorkem
>>
>>
>> On Fri, Apr 18, 2014 at 10:21 PM, Michal Mocny <mmo...@chromium.org>
>> wrote:
>>
>> > On Fri, Apr 18, 2014 at 4:11 PM, Gorkem Ercan <gorkem.er...@gmail.com
>> > >wrote:
>> >
>> > > On Thu, Apr 17, 2014 at 3:46 PM, Michal Mocny <mmo...@chromium.org>
>> > wrote:
>> > >
>> > > > Took a quick glance.  General questions:
>> > > > - why the need for save?  Why not just alter the list on each
>> cordova
>> > > > plugin add/rm?
>> > > >
>> > >
>> > > I do not want to force this workflow. Calling save does not bring much
>> > > overhead, considering plugin list does not probably change constantly.
>> > >
>> >
>> > If you are going to make this choice, then I would not like to
>> > automatically install on prepare.  There should be an explicit "load"
>> > command.  (those names are wrong, but you get the point).
>> >
>> > Either we automatically manage plugin installs, or explicitly manage
>> them.
>> >  I'm happy to start with explicit and support opt-in to automatic
>> handling
>> > later once we have confidence in the feature.
>> >
>> >
>> > >
>> > >
>> > > > - wouldn't "cordova plugin rm foo && cordova prepare" re-install
>> that
>> > > > plugin right now?
>> > > >
>> > >
>> > > This workflow would require an explicit update to config.xml if a
>> plugin
>> > is
>> > > persisted in there. This is a good point, I shall update plugins rm to
>> > > print a warning about it.
>> >
>> >
>> > >
>> > > > - why the name <feature> and not <dependency> ?  I think this
>> > > functionality
>> > > > should overlap with the plugin.xml spec.
>> > > >
>> > > >
>> > > feature tag lives in the w3c widget spec which has advantages for
>> those
>> > > (like myself)  who does provide tools for editing config,xml.
>> > >
>> >
>> > We are no longer using that spec, and I as we move more functionality
>> from
>> > plugins.xml into config.xml we should strive to keep them in line.  It
>> also
>> > makes our docs easier.
>> >
>> >
>> > >
>> > >
>> > >
>> > > > I haven't put the PR through testing yet.
>> > > >
>> > > >
>> > > > On Thu, Apr 17, 2014 at 5:54 PM, Jesse <purplecabb...@gmail.com>
>> > wrote:
>> > > >
>> > > > > Yeah, that looks weird.
>> > > > >
>> > > > > @purplecabbage
>> > > > > risingj.com
>> > > > >
>> > > > >
>> > > > > On Thu, Apr 17, 2014 at 2:46 PM, kamrik <g...@git.apache.org>
>> wrote:
>> > > > >
>> > > > > > Github user kamrik commented on a diff in the pull request:
>> > > > > >
>> > > > > >
>> > > > https://github.com/apache/cordova-cli/pull/165#discussion_r11755812
>> > > > > >
>> > > > > >     --- Diff: src/save.js ---
>> > > > > >     @@ -0,0 +1,71 @@
>> > > > > >     +/**
>> > > > > >     +    Licensed to the Apache Software Foundation (ASF) under
>> one
>> > > > > >     +    or more contributor license agreements.  See the NOTICE
>> > file
>> > > > > >     +    distributed with this work for additional information
>> > > > > >     +    regarding copyright ownership.  The ASF licenses this
>> file
>> > > > > >     +    to you under the Apache License, Version 2.0 (the
>> > > > > >     +    "License"); you may not use this file except in
>> compliance
>> > > > > >     +    with the License.  You may obtain a copy of the
>> License at
>> > > > > >     +
>> > > > > >     +    http://www.apache.org/licenses/LICENSE-2.0
>> > > > > >     +
>> > > > > >     +    Unless required by applicable law or agreed to in
>> writing,
>> > > > > >     +    software distributed under the License is distributed
>> on
>> > an
>> > > > > >     +    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
>> > > > > >     +    KIND, either express or implied.  See the License for
>> the
>> > > > > >     +    specific language governing permissions and limitations
>> > > > > >     +    under the License.
>> > > > > >     +*/
>> > > > > >     +
>> > > > > >     +var cordova_util     = require('./util'),
>> > > > > >     +    ConfigParser     = require('./ConfigParser'),
>> > > > > >     +    path             = require('path'),
>> > > > > >     +    xml              =require('./xml-helpers')
>> > > > > >     +    Q                = require('q'),
>> > > > > >     +    events           = require('./events');
>> > > > > >     +
>> > > > > >     +;
>> > > > > >     +
>> > > > > >     +
>> > > > > >     +module.exports = function save(target){
>> > > > > >     +  var projectHome = cordova_util.cdProjectRoot();
>> > > > > >     +  var configPath = cordova_util.projectConfig(projectHome);
>> > > > > >     +  var configXml = new ConfigParser(configPath);
>> > > > > >     +  var pluginsPath = path.join(projectHome, 'plugins');
>> > > > > >     +  var plugins = cordova_util.findPlugins(pluginsPath);
>> > > > > >     +
>> > > > > >     +  return Q.all(plugins.map(function(plugin){
>> > > > > >     +    var currentPluginPath = path.join(pluginsPath,plugin);
>> > > > > >     +    var name = readPluginName(currentPluginPath);
>> > > > > >     +    var id = plugin;
>> > > > > >     +    var version = readPluginVersion(currentPluginPath);
>> > > > > >     +    var features = configXml.doc.findall('feature');
>> > > > > >     +      for(var i=0; i<features.length; i++){
>> > > > > >     +        if(features[i].attrib.name === name){
>> > > > > >     +          events.emit('results', 'An entry for "'+ plugin+
>> '"
>> > > > > already
>> > > > > > exists');
>> > > > > >     +          return Q();
>> > > > > >     +        }
>> > > > > >     +      }
>> > > > > >     +    configXml.addFeature(name, JSON.parse('[{"name":"id",
>> > > > > > "value":"'+id+'"},{"name":"version", "value":"'+version+'"}]'));
>> > > > > >     --- End diff --
>> > > > > >
>> > > > > >     I might be missing something, but why JSON.parse() rather
>> than
>> > > just
>> > > > > > literal array of objects?
>> > > > > >
>> > > > > >
>> > > > > > ---
>> > > > > > If your project is set up for it, you can reply to this email
>> and
>> > > have
>> > > > > your
>> > > > > > reply appear on GitHub as well. If your project does not have
>> this
>> > > > > feature
>> > > > > > enabled and wishes so, or if the feature is enabled but not
>> > working,
>> > > > > please
>> > > > > > contact infrastructure at infrastruct...@apache.org or file a
>> JIRA
>> > > > > ticket
>> > > > > > with INFRA.
>> > > > > > ---
>> > > > > >
>> > > > >
>> > > >
>> > >
>> >
>>
>
>

Reply via email to