<?xml version="1.0" encoding="UTF-8" ?>
<rdf:RDF xmlns="http://usefulinc.com/ns/doap#" xmlns:foaf="http://xmlns.com/foaf/0.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><Project><name>buildout.eggnest</name>
<shortdesc>buildout extension to auto install eggs</shortdesc>
<description>buildout.eggnest
=================


The problem
-----------
It is very convenient to install functionality to a buildout with just
a couple of lines, but for those used to Zope 2, one might miss
the way of adding a piece of functionality by just dropping something
(i.e. a product) into a folder. This could be good for people who want to try
out things without worrying about editing a configuration file with
lots of directives in it.
When wanting to install a new egg using buildout you currently have to
edit the buildout configuration file(s) and add a couple of lines in 
the right places. What if you could just drop a file in a folder instead?

Solution
--------
Make a buildout extension so that the only thing you need to do in order to install an egg
is to take a simple text file, and drop it in a certain directory. When you rerun buildout the
contents of that file is parsed, the specified egg is downloaded and added to the instance.

When ``buildout.eggnest`` is run it::

  1. If ``eggnest-src-directory`` is not given the default directory ``src``
     is scanned.  
  
  2. Adds the egg to the ``eggs`` and ``zcml`` option to a set of given buildout parts.

This steps are done on the fly when running buildout. So I can add/delete/rename
an egg and it will be picked up.

NOTE: The extension does not write to the buildout's configuration file.

buildout.eggnest options
-------------------------

eggnest-src-directory:
 Specified to the directory that your egg install files should be placed.
 Defaults to src. An idea could be
 to have a dedicated directory called "eggnest".
				
eggnest-parts:
 What part of your buildout config that the eggs should be added to. *required*	

eggnest-verbose:
 Set this to ``true`` to get more information. 
 Not really that much right now but a little bit more at least.


How to use it
-------------

To use ``buildout.eggnest`` you need to add the following to your buildout.cfg::

  [buildout]
  extensions = 
    buildout.eggnest

  eggnest-parts = 
    instance

In ``eggnest-parts`` you need to specify what buildout part that the eggs should be added to. 
By default the ``src`` directory is scanned for egg specification files. 


eggs specification files for eggnest
------------------------------------

The egg install specification files should have this structure. This is the same as the normal buildout config format.::

  [eggnest]
  egg = 
    plone.introspector
  
  zcml = 
    plone.introspector
	
``zcml`` can be multiple lines if additional slugs need to be specified.

If the egg is in the ``Products`` namespace the zcml is not needed in the specification file.::

  [eggnest]
  egg = 
    Products.DocFinderTab



buildout.eggnest was created by Martin Lundwall &lt;martin@webworks.se&gt; after an 
initial idea by Jorgen Modin &lt;jorgen@webworks.se&gt;

Change history
================

0.1 (unreleased)
----------------


Detailed Documentation
======================

Tests for buildout.eggnest buildout extension
-------------------------------------------------

Let's create a buildout configuration file::

    &gt;&gt;&gt; data = """
    ... [buildout]
    ... parts = zope2 instance1 instance2 instance3
    ... extensions = 
    ... eggs =
    ... develop = %s
    ... eggnest-parts = instance1 instance2
    ... [instance1]
    ... recipe = plone.recipe.zope2instance
    ... zope2-location = ${zope2:location}
    ... user = admin:admin
    ... [instance2]
    ... recipe = plone.recipe.zope2instance
    ... zope2-location = ${zope2:location}
    ... user = admin:admin
    ... [instance3]
    ... recipe = plone.recipe.zope2instance
    ... zope2-location = ${zope2:location}
    ... user = admin:admin
    ... [zope2]
    ... recipe = plone.recipe.zope2install
    ... url = http://www.zope.org/Products/Zope/2.9.8/Zope-2.9.8-final.tgz
    ... """ % egg_dir
    &gt;&gt;&gt; rmdir(tempdir, 'buildout.test')
    &gt;&gt;&gt; cd(tempdir)
    &gt;&gt;&gt; sh('mkdir buildout.test')
    mkdir buildout.test
    &lt;BLANKLINE&gt;
    &gt;&gt;&gt; cd('buildout.test')
    &gt;&gt;&gt; touch('buildout.cfg', data=data)
    &gt;&gt;&gt; ls('.')
    buildout.cfg

run the buildout first time so we get our zope instances::

    &gt;&gt;&gt; sh('svn export svn://svn.zope.org/repos/main/zc.buildout/trunk/bootstrap/bootstrap.py')
    svn export svn://svn.zope.org/repos/main/zc.buildout/trunk/bootstrap/bootstrap.py
    A    bootstrap.py
    Export complete.
    &lt;BLANKLINE&gt;
    &gt;&gt;&gt; sh('python2.4 bootstrap.py')
    python2.4 bootstrap.py
    ... 
    Creating directory '/private/tmp/buildout.test/bin'.
    Creating directory '/private/tmp/buildout.test/parts'.
    Creating directory '/private/tmp/buildout.test/develop-eggs'.
    Generated script '/private/tmp/buildout.test/bin/buildout'.
    &lt;BLANKLINE&gt;
    
    &gt;&gt;&gt; sh('./bin/buildout')
    ./bin/buildout
    ...
    Installing instance1.
    ...
    Installing instance2.
    ...
    Installing instance3.
    ...
    &lt;BLANKLINE&gt;
    &lt;BLANKLINE&gt;
    
Now let's create a test products specification file and add the buildout.eggnest as an extension::
	
    &gt;&gt;&gt; sh('mkdir src')
    mkdir src
    &lt;BLANKLINE&gt;
    &gt;&gt;&gt; product = """
    ... [eggnest]
    ... egg = 
    ...     plone.portlet.static
    ... zcml = 
    ...     plone.portlet.static
    ... """    
    &gt;&gt;&gt; touch('src/product.txt', data=product)
    &gt;&gt;&gt; ls('src')
    product.txt
    &gt;&gt;&gt; data = data.replace('extensions =', 'extensions = buildout.eggnest')
    &gt;&gt;&gt; touch('buildout.cfg', data=data)
 

Ok, so now that we have an egg, lets run the buildout in offline mode. We 
should get a zcml slugs in parts/instance1/etc/package-includes and parts/instance2/etc/package-includes
but not in parts/instance3/etc/package-includes,
and a line with the path to our egg in the bin/instance1 and bin/instance2
but not in bin/instance3 files. 

First we check that there is nothing of the previous mentioned things::

    &gt;&gt;&gt; ls('develop-eggs')
    buildout.eggnest.egg-link
    &gt;&gt;&gt; ls('parts/instance1/etc/package-includes') 
    No directory named parts/instance1/etc/package-includes

    &gt;&gt;&gt; ls('parts/instance2/etc/package-includes') 
    No directory named parts/instance2/etc/package-includes
    
    &gt;&gt;&gt; ls('parts/instance3/etc/package-includes') 
    No directory named parts/instance3/etc/package-includes

    &gt;&gt;&gt; sh('grep plone.portlet.static bin/instance1')
    grep plone.portlet.static bin/instance1
    &lt;BLANKLINE&gt;
 
    &gt;&gt;&gt; sh('grep plone.portlet.static bin/instance2')
    grep plone.portlet.static bin/instance2
    &lt;BLANKLINE&gt;

    &gt;&gt;&gt; sh('grep plone.portlet.static bin/instance3')
    grep plone.portlet.static bin/instance3
    &lt;BLANKLINE&gt;

OK, now run the buildout in offline mode::
   
    &gt;&gt;&gt; sh('./bin/buildout')
    ./bin/buildout
    ...

Check that we have a correct created buildout. 

Check that we have our zcml slugs in the package-includes::

    &gt;&gt;&gt; ls('parts', 'instance1', 'etc', 'package-includes')
    001-plone.portlet.static-configure.zcml
 
    &gt;&gt;&gt; ls('parts', 'instance2', 'etc', 'package-includes')
    001-plone.portlet.static-configure.zcml

    &gt;&gt;&gt; ls('parts', 'instance3', 'etc', 'package-includes')
    No directory named parts/instance3/etc/package-includes
    

and in the end check that there is a line in bin/instance1 and bin/instance1
that includes our egg in the path::

    &gt;&gt;&gt; code = cat('bin', 'instance1', returndata=True)
    &gt;&gt;&gt; code.find('plone.portlet.static') == -1
    False

    &gt;&gt;&gt; code = cat('bin', 'instance2', returndata=True)
    &gt;&gt;&gt; code.find('plone.portlet.static') == -1
    False
    
    &gt;&gt;&gt; code = cat('bin', 'instance3', returndata=True)
    &gt;&gt;&gt; code.find('plone.portlet.static') == -1
    True
    
Let's now try the ``eggnest-src-directory`` option. We create a new buildout.cfg file
with an empty ``eggnest-src-directory``::

    &gt;&gt;&gt; data = data.replace('eggs =', 'eggnest-src-directory = \neggs = ')
    &gt;&gt;&gt; touch('buildout.cfg', data=data)
    &gt;&gt;&gt; sh('./bin/buildout')
    ./bin/buildout
    ...


No zcml slug in the instance 1,2 or 3:: 

    &gt;&gt;&gt; ls('parts', 'instance1', 'etc', 'package-includes')
    No directory named parts/instance1/etc/package-includes

    &gt;&gt;&gt; ls('parts', 'instance2', 'etc', 'package-includes')
    No directory named parts/instance2/etc/package-includes

    &gt;&gt;&gt; ls('parts', 'instance3', 'etc', 'package-includes')
    No directory named parts/instance3/etc/package-includes

Nor a line in bin/instance1, bin/instance2 or bin/instance3 with our egg path::

    &gt;&gt;&gt; code = cat('bin', 'instance1', returndata=True)
    &gt;&gt;&gt; code.find('plone.portlet.static') == -1
    True

    &gt;&gt;&gt; code = cat('bin', 'instance2', returndata=True)
    &gt;&gt;&gt; code.find('plone.portlet.static') == -1
    True

    &gt;&gt;&gt; code = cat('bin', 'instance3', returndata=True)
    &gt;&gt;&gt; code.find('plone.portlet.static') == -1
    True

But if the ``eggnest-src-directory`` option is not empty::

    &gt;&gt;&gt; data = data.replace('eggnest-src-directory =', 'eggnest-src-directory = auto')
    &gt;&gt;&gt; touch('buildout.cfg', data=data)
    &gt;&gt;&gt; sh('rm src/product.txt')
    rm src/product.txt
    &gt;&gt;&gt; sh('mkdir auto')
    mkdir auto
    &gt;&gt;&gt; touch('auto/product.txt', data=product)
    &gt;&gt;&gt; ls('auto')
    product.txt
    &gt;&gt;&gt; sh('./bin/buildout -o')
    ./bin/buildout -o
    ...

and we get a zcml slug only in the specified target::

    &gt;&gt;&gt; ls('parts', 'instance1', 'etc', 'package-includes')
    001-plone.portlet.static-configure.zcml
 
    &gt;&gt;&gt; ls('parts', 'instance2', 'etc', 'package-includes')
    001-plone.portlet.static-configure.zcml

    &gt;&gt;&gt; ls('parts', 'instance3', 'etc', 'package-includes')
    No directory named parts/instance3/etc/package-includes
    
and only the specified target's control script is updated::

    &gt;&gt;&gt; code = cat('bin', 'instance1', returndata=True)
    &gt;&gt;&gt; code.find('plone.portlet.static') == -1
    False

    &gt;&gt;&gt; code = cat('bin', 'instance2', returndata=True)
    &gt;&gt;&gt; code.find('plone.portlet.static') == -1
    False
    
    &gt;&gt;&gt; code = cat('bin', 'instance3', returndata=True)
    &gt;&gt;&gt; code.find('plone.portlet.static') == -1
    True</description>
<homepage rdf:resource="http://svn.plone.org/svn/collective/buildout/buildout.eggnest" />
<maintainer><foaf:Person><foaf:name>Martin Lundwall</foaf:name>
<foaf:mbox_sha1sum>d0008f0befb0bad60f535e2cc7338c44e98d7c2b</foaf:mbox_sha1sum></foaf:Person></maintainer>
<release><Version><revision>0.1</revision></Version></release>
</Project></rdf:RDF>