Hi,

i'm writing a galaxy wrapper for bismark and trim-galore. Both are plain
perl scripts, that wraps around other dependencies (e.g. Bowtie). The
idea was to include the perl-scripts directly in the galaxy-wrapper and
update the PATH to the REPOSITORY_INSTALL_DIR in the tool_dependency.xml
file.

----
<package name="bismark" version="0.7.7">
    <install version="1.0">
        <actions>
            <action type="set_environment">
                <environment_variable name="PATH"
action="prepend_to">$REPOSITORY_INSTALL_DIR</environment_variable>
            </action>
        </actions>
   </install>
   <readme>
bismark, bismark_genome_preparation and bismark_methylation_extractor
are shipped with that wrapper
   </readme>
</package>
----

Unfortunately, that was not supported because the toolshed expected at
least one "action_type". The attached patch should add that feature.

Furthermore, bowtie2 is only available as zip archive and afaik that was
not handled in the toolshed. The attached patch also added
check_zipfile(), extract_zip() and zip_extraction_directory() to fully
support zip archives.

Thanks!
Bjoern

diff -r cc45bdf57b66 lib/galaxy/tool_shed/tool_dependencies/common_util.py
--- a/lib/galaxy/tool_shed/tool_dependencies/common_util.py	Fri Oct 12 17:22:51 2012 -0400
+++ b/lib/galaxy/tool_shed/tool_dependencies/common_util.py	Mon Oct 15 18:30:58 2012 +0200
@@ -1,12 +1,13 @@
 import os, shutil, tarfile, urllib2
 from galaxy.datatypes.checkers import *
+from zipfile import ZipFile
 
 def create_env_var_dict( elem, tool_dependency_install_dir=None, tool_shed_repository_install_dir=None ):
     env_var_name = elem.get( 'name', 'PATH' )
     env_var_action = elem.get( 'action', 'prepend_to' )
     env_var_text = None
     if elem.text and elem.text.find( 'REPOSITORY_INSTALL_DIR' ) >= 0:
-        if tool_shed_repository_install_dir:
+        if tool_shed_repository_install_dir and elem.text.find( '$REPOSITORY_INSTALL_DIR' ) != -1:
             env_var_text = elem.text.replace( '$REPOSITORY_INSTALL_DIR', tool_shed_repository_install_dir )
             return dict( name=env_var_name, action=env_var_action, value=env_var_text )
         else:
@@ -50,6 +51,27 @@
         tar = tarfile.open( file_name )
     tar.extractall( path=file_path )
     tar.close()
+def check_zipfile( archive_name ):
+    """
+        This function is a bit pedantic and not functionally necessary.
+        It checks whether there is no file pointing outside of the extraction, 
+        because ZipFile.extractall() has some potential security holes.
+        See python zipfile documentation for more details.
+    """
+    basename = os.path.normcase( os.path.realpath( os.path.dirname( archive_name ) ) )
+    with ZipFile( archive_name ) as zip:
+        for member in ZipFile.namelist(zip):
+            member_path = os.path.normcase( os.path.realpath( os.path.join(basename, member) ) )
+            if not member_path.startswith( basename ):
+                return False
+    return True
+def extract_zip( archive_path, extraction_path ):
+    if not check_zipfile( archive_path ):
+        return False
+    zip = ZipFile( archive_path )
+    zip.extractall( extraction_path )
+    zip.close()
+    return True
 def isbz2( file_path ):
     return is_bz2( file_path )
 def isgzip( file_path ):
@@ -87,6 +109,21 @@
     if os.path.exists( os.path.abspath( os.path.join( file_path, file_name ) ) ):
         return os.path.abspath( os.path.join( file_path, file_name ) )
     raise ValueError( 'Could not find directory %s' % os.path.abspath( os.path.join( file_path, file_name[ :-len( extension ) ] ) ) )
+def zip_extraction_directory( file_path, file_name ):
+    """
+        Try to guess the extraction directory.
+        1. Is there one folder inside the working directory.
+        2. Are there more than one folder/files return working directory.
+    """
+    files = [filename for filename in os.listdir( file_path ) if not filename.endswith('.zip')]
+    if len(files) > 1:
+        return os.path.abspath( file_path )
+    elif len(files) == 1:
+        # if there is only on file it should be a folder
+        if os.path.isdir( os.path.join( file_path, files[0] ) ):
+            return os.path.abspath( os.path.join( file_path, files[0] ) )
+    # otherwise we assume that the download or something else went wrong
+    raise ValueError( 'Could not find directory for the extracted file %s' % os.path.abspath( os.path.join( file_path, file_name ) ) )
 def url_download( install_dir, downloaded_file_name, download_url ):
     file_path = os.path.join( install_dir, downloaded_file_name )
     src = None
diff -r cc45bdf57b66 lib/galaxy/tool_shed/tool_dependencies/fabric_util.py
--- a/lib/galaxy/tool_shed/tool_dependencies/fabric_util.py	Fri Oct 12 17:22:51 2012 -0400
+++ b/lib/galaxy/tool_shed/tool_dependencies/fabric_util.py	Mon Oct 15 18:30:58 2012 +0200
@@ -58,6 +58,9 @@
                 # The first action in the list of actions will be the one that defines the installation process.  There
                 # are currently only two supported processes; download_by_url and clone via a "shell_command" action type.
                 action_type, action_dict = actions[ 0 ]
+                # initialize dir for cases where no action_type is specified
+                # that can happend if a developer only whant's to change the PATH variable, because the dependency is shipped with the package
+                dir = os.path.abspath(work_dir)
                 if action_type == 'download_by_url':
                     # <action type="download_by_url">http://sourceforge.net/projects/samtools/files/samtools/0.1.18/samtools-0.1.18.tar.bz2</action>
                     url = action_dict[ 'url' ]
@@ -66,19 +69,26 @@
                     if common_util.istar( downloaded_file_path ):
                         common_util.extract_tar( downloaded_file_path, work_dir )
                         dir = common_util.tar_extraction_directory( work_dir, downloaded_filename )
+                    elif common_util.iszip( downloaded_file_path ):
+                        if not common_util.extract_zip( downloaded_file_path, work_dir ):
+                        dir = common_util.zip_extraction_directory( work_dir, downloaded_filename )
                     else:
                         dir = work_dir
+                    # remove first action from the list
+                    actions.pop(0)
                 elif action_type == 'shell_command':
                     # <action type="shell_command">git clone --recursive git://github.com/ekg/freebayes.git</action>
                     return_code = handle_command( app, tool_dependency, install_dir, action_dict[ 'command' ] )
                     if return_code:
                         return
                     dir = package_name
+                    # remove first action from the list
+                    actions.pop(0)
                 if not os.path.exists( dir ):
                     os.makedirs( dir )
                 # The package has been down-loaded, so we can now perform all of the actions defined for building it.
-                with lcd( dir ):                
-                    for action_tup in actions[ 1: ]:
+                with lcd( dir ):
+                    for action_tup in actions:
                         action_type, action_dict = action_tup
                         current_dir = os.path.abspath( os.path.join( work_dir, dir ) )
                         if action_type == 'make_directory':
diff -r cc45bdf57b66 lib/galaxy/tool_shed/tool_dependencies/install_util.py
--- a/lib/galaxy/tool_shed/tool_dependencies/install_util.py	Fri Oct 12 17:22:51 2012 -0400
+++ b/lib/galaxy/tool_shed/tool_dependencies/install_util.py	Mon Oct 15 18:30:58 2012 +0200
@@ -89,7 +89,8 @@
                         if package_install_version == '1.0':
                             # Handle tool dependency installation using a fabric method included in the Galaxy framework.
                             for actions_elem in package_elem:
-                                install_via_fabric( app, tool_dependency, actions_elem, install_dir, package_name=package_name )
+                                tool_shed_repository_install_dir = get_tool_shed_repository_install_dir( app, tool_shed_repository )
+                                install_via_fabric( app, tool_dependency, actions_elem, install_dir, package_name=package_name, tool_shed_repository_install_dir=tool_shed_repository_install_dir )
                                 sa_session.refresh( tool_dependency )
                                 if tool_dependency.status != app.model.ToolDependency.installation_status.ERROR:
                                     print package_name, 'version', package_version, 'installed in', install_dir
@@ -114,6 +115,7 @@
     return tool_dependency
 def install_via_fabric( app, tool_dependency, actions_elem, install_dir, package_name=None, proprietary_fabfile_path=None, **kwd ):
     """Parse a tool_dependency.xml file's <actions> tag set to gather information for the installation via fabric."""
+    tool_shed_repository_install_dir = kwd['tool_shed_repository_install_dir']
     sa_session = app.model.context.current
     if not os.path.exists( install_dir ):
         os.makedirs( install_dir )
@@ -164,7 +166,7 @@
             env_var_dicts = []
             for env_elem in action_elem:
                 if env_elem.tag == 'environment_variable':
-                    env_var_dict = create_env_var_dict( env_elem, tool_dependency_install_dir=install_dir )
+                    env_var_dict = create_env_var_dict( env_elem, tool_dependency_install_dir=install_dir, tool_shed_repository_install_dir=tool_shed_repository_install_dir )
                     if env_var_dict:
                         env_var_dicts.append( env_var_dict )  
             if env_var_dicts:
___________________________________________________________
Please keep all replies on the list by using "reply all"
in your mail client.  To manage your subscriptions to this
and other Galaxy lists, please use the interface at:

  http://lists.bx.psu.edu/

Reply via email to