mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Eliminate the use of the Galaxy web transaction object in Tool Shed utility functions related to importing a repository capsule into a Tool Shed and move som hg related functions from the commit_util module to the hg_util module.
This commit is contained in:
@@ -419,7 +419,7 @@ class ToolShedRepositoriesController( BaseAPIController ):
|
||||
try:
|
||||
invalid_file_tups, metadata_dict = metadata_util.reset_all_metadata_on_installed_repository( trans.app, repository_id )
|
||||
if invalid_file_tups:
|
||||
message = tool_util.generate_message_for_invalid_tools( trans,
|
||||
message = tool_util.generate_message_for_invalid_tools( trans.app,
|
||||
invalid_file_tups,
|
||||
repository,
|
||||
None,
|
||||
|
||||
@@ -250,8 +250,8 @@ class RepositoriesController( BaseAPIController ):
|
||||
return {}
|
||||
capsule_dict[ 'tar_archive' ] = tar_archive
|
||||
capsule_dict[ 'capsule_file_name' ] = capsule_file_name
|
||||
capsule_dict = import_util.extract_capsule_files( trans, **capsule_dict )
|
||||
capsule_dict = import_util.validate_capsule( trans, **capsule_dict )
|
||||
capsule_dict = import_util.extract_capsule_files( **capsule_dict )
|
||||
capsule_dict = import_util.validate_capsule( **capsule_dict )
|
||||
status = capsule_dict.get( 'status', 'error' )
|
||||
if status == 'error':
|
||||
log.debug( 'The capsule contents are invalid and cannot be imported:<br/>%s' % \
|
||||
@@ -268,7 +268,10 @@ class RepositoriesController( BaseAPIController ):
|
||||
# The manifest.xml file has already been validated, so no error_message should be returned here.
|
||||
repository_info_dicts, error_message = import_util.get_repository_info_from_manifest( manifest_file_path )
|
||||
# Determine the status for each exported repository archive contained within the capsule.
|
||||
repository_status_info_dicts = import_util.get_repository_status_from_tool_shed( trans, repository_info_dicts )
|
||||
repository_status_info_dicts = import_util.get_repository_status_from_tool_shed( trans.app,
|
||||
trans.user,
|
||||
trans.user_is_admin(),
|
||||
repository_info_dicts )
|
||||
# Generate a list of repository name / import results message tuples for display after the capsule is imported.
|
||||
import_results_tups = []
|
||||
# Only create repositories that do not yet exist and that the current user is authorized to create. The
|
||||
@@ -277,11 +280,12 @@ class RepositoriesController( BaseAPIController ):
|
||||
# Add the capsule_file_name and encoded_file_path to the repository_status_info_dict.
|
||||
repository_status_info_dict[ 'capsule_file_name' ] = capsule_file_name
|
||||
repository_status_info_dict[ 'encoded_file_path' ] = encoded_file_path
|
||||
import_results_tups = \
|
||||
repository_maintenance_util.create_repository_and_import_archive( trans,
|
||||
repository_status_info_dict,
|
||||
import_results_tups )
|
||||
import_util.check_status_and_reset_downloadable( trans, import_results_tups )
|
||||
import_results_tups = import_util.create_repository_and_import_archive( trans.app,
|
||||
trans.request.host,
|
||||
trans.user,
|
||||
repository_status_info_dict,
|
||||
import_results_tups )
|
||||
import_util.check_status_and_reset_downloadable( trans.app, import_results_tups )
|
||||
basic_util.remove_dir( file_path )
|
||||
# NOTE: the order of installation is defined in import_results_tups, but order will be lost
|
||||
# when transferred to return_dict.
|
||||
@@ -413,15 +417,22 @@ class RepositoriesController( BaseAPIController ):
|
||||
log.debug( "Resetting metadata on repository %s" % str( repository.name ) )
|
||||
repository_id = trans.security.encode_id( repository.id )
|
||||
try:
|
||||
invalid_file_tups, metadata_dict = metadata_util.reset_all_metadata_on_repository_in_tool_shed( trans, repository_id )
|
||||
invalid_file_tups, metadata_dict = \
|
||||
metadata_util.reset_all_metadata_on_repository_in_tool_shed( trans, repository_id )
|
||||
if invalid_file_tups:
|
||||
message = tool_util.generate_message_for_invalid_tools( trans, invalid_file_tups, repository, None, as_html=False )
|
||||
message = tool_util.generate_message_for_invalid_tools( trans.app,
|
||||
invalid_file_tups,
|
||||
repository,
|
||||
None,
|
||||
as_html=False )
|
||||
results[ 'unsuccessful_count' ] += 1
|
||||
else:
|
||||
message = "Successfully reset metadata on repository %s owned by %s" % ( str( repository.name ), str( repository.user.username ) )
|
||||
message = "Successfully reset metadata on repository %s owned by %s" % \
|
||||
( str( repository.name ), str( repository.user.username ) )
|
||||
results[ 'successful_count' ] += 1
|
||||
except Exception, e:
|
||||
message = "Error resetting metadata on repository %s owned by %s: %s" % ( str( repository.name ), str( repository.user.username ), str( e ) )
|
||||
message = "Error resetting metadata on repository %s owned by %s: %s" % \
|
||||
( str( repository.name ), str( repository.user.username ), str( e ) )
|
||||
results[ 'unsuccessful_count' ] += 1
|
||||
status = '%s : %s' % ( str( repository.name ), message )
|
||||
results[ 'repository_status' ].append( status )
|
||||
@@ -486,14 +497,21 @@ class RepositoriesController( BaseAPIController ):
|
||||
results = dict( start_time=start_time,
|
||||
repository_status=[] )
|
||||
try:
|
||||
invalid_file_tups, metadata_dict = metadata_util.reset_all_metadata_on_repository_in_tool_shed( trans,
|
||||
trans.security.encode_id( repository.id ) )
|
||||
invalid_file_tups, metadata_dict = \
|
||||
metadata_util.reset_all_metadata_on_repository_in_tool_shed( trans,
|
||||
trans.security.encode_id( repository.id ) )
|
||||
if invalid_file_tups:
|
||||
message = tool_util.generate_message_for_invalid_tools( trans, invalid_file_tups, repository, None, as_html=False )
|
||||
message = tool_util.generate_message_for_invalid_tools( trans.app,
|
||||
invalid_file_tups,
|
||||
repository,
|
||||
None,
|
||||
as_html=False )
|
||||
else:
|
||||
message = "Successfully reset metadata on repository %s owned by %s" % ( str( repository.name ), str( repository.user.username ) )
|
||||
message = "Successfully reset metadata on repository %s owned by %s" % \
|
||||
( str( repository.name ), str( repository.user.username ) )
|
||||
except Exception, e:
|
||||
message = "Error resetting metadata on repository %s owned by %s: %s" % ( str( repository.name ), str( repository.user.username ), str( e ) )
|
||||
message = "Error resetting metadata on repository %s owned by %s: %s" % \
|
||||
( str( repository.name ), str( repository.user.username ), str( e ) )
|
||||
status = '%s : %s' % ( str( repository.name ), message )
|
||||
results[ 'repository_status' ].append( status )
|
||||
return results
|
||||
|
||||
@@ -315,11 +315,14 @@ class AdminController( BaseUIController, Admin ):
|
||||
def manage_role_associations( self, trans, **kwd ):
|
||||
"""Manage users, groups and repositories associated with a role."""
|
||||
role_id = kwd.get( 'id', None )
|
||||
role = repository_maintenance_util.get_role_by_id( trans, role_id )
|
||||
role = repository_maintenance_util.get_role_by_id( trans.app, role_id )
|
||||
# We currently only have a single role associated with a repository, the repository admin role.
|
||||
repository_role_association = role.repositories[ 0 ]
|
||||
repository = repository_role_association.repository
|
||||
associations_dict = repository_maintenance_util.handle_role_associations( trans, role, repository, **kwd )
|
||||
associations_dict = repository_maintenance_util.handle_role_associations( trans.app,
|
||||
role,
|
||||
repository,
|
||||
**kwd )
|
||||
in_users = associations_dict.get( 'in_users', [] )
|
||||
out_users = associations_dict.get( 'out_users', [] )
|
||||
in_groups = associations_dict.get( 'in_groups', [] )
|
||||
|
||||
@@ -45,7 +45,7 @@ class HgController( BaseUIController ):
|
||||
repo = hg.repository( ui.ui(), repository.repo_path( trans.app ) )
|
||||
update_repository( repo, ctx_rev=None )
|
||||
# Set metadata using the repository files on disk.
|
||||
error_message, status = set_repository_metadata( trans, repository )
|
||||
error_message, status = set_repository_metadata( trans.app, trans.request.host, trans.user, repository )
|
||||
if status == 'ok' and error_message:
|
||||
log.debug( "Successfully reset metadata on repository %s owned by %s, but encountered problem: %s" % \
|
||||
( str( repository.name ), str( repository.user.username ), error_message ) )
|
||||
|
||||
@@ -41,7 +41,6 @@ import tool_shed.repository_types.util as rt_util
|
||||
from galaxy import eggs
|
||||
eggs.require( 'mercurial' )
|
||||
|
||||
from mercurial import commands
|
||||
from mercurial import mdiff
|
||||
from mercurial import patch
|
||||
|
||||
@@ -849,7 +848,7 @@ class RepositoryController( BaseUIController, ratings_util.ItemRatings ):
|
||||
operation = kwd[ 'operation' ].lower()
|
||||
if operation == "preview_tools_in_changeset":
|
||||
repository = suc.get_repository_in_tool_shed( trans.app, repository_id )
|
||||
repository_metadata = metadata_util.get_latest_repository_metadata( trans, repository.id, downloadable=True )
|
||||
repository_metadata = metadata_util.get_latest_repository_metadata( trans.app, repository.id, downloadable=True )
|
||||
latest_installable_changeset_revision = repository_metadata.changeset_revision
|
||||
return trans.response.send_redirect( web.url_for( controller='repository',
|
||||
action='preview_tools_in_changeset',
|
||||
@@ -1017,7 +1016,7 @@ class RepositoryController( BaseUIController, ratings_util.ItemRatings ):
|
||||
if error:
|
||||
status = 'error'
|
||||
else:
|
||||
repository, message = repository_maintenance_util.create_repository( trans,
|
||||
repository, message = repository_maintenance_util.create_repository( trans.app,
|
||||
name,
|
||||
repository_type,
|
||||
description,
|
||||
@@ -1974,7 +1973,10 @@ class RepositoryController( BaseUIController, ratings_util.ItemRatings ):
|
||||
# The manifest.xml file has already been validated, so no error_message should be returned here.
|
||||
repository_info_dicts, error_message = import_util.get_repository_info_from_manifest( manifest_file_path )
|
||||
# Determine the status for each exported repository archive contained within the capsule.
|
||||
repository_status_info_dicts = import_util.get_repository_status_from_tool_shed( trans, repository_info_dicts )
|
||||
repository_status_info_dicts = import_util.get_repository_status_from_tool_shed( trans.app,
|
||||
trans.user,
|
||||
trans.user_is_admin(),
|
||||
repository_info_dicts )
|
||||
if 'import_capsule_button' in kwd:
|
||||
# Generate a list of repository name / import results message tuples for display after the capsule is imported.
|
||||
import_results_tups = []
|
||||
@@ -1984,11 +1986,12 @@ class RepositoryController( BaseUIController, ratings_util.ItemRatings ):
|
||||
# Add the capsule_file_name and encoded_file_path to the repository_status_info_dict.
|
||||
repository_status_info_dict[ 'capsule_file_name' ] = capsule_file_name
|
||||
repository_status_info_dict[ 'encoded_file_path' ] = encoded_file_path
|
||||
import_results_tups = \
|
||||
repository_maintenance_util.create_repository_and_import_archive( trans,
|
||||
repository_status_info_dict,
|
||||
import_results_tups )
|
||||
import_util.check_status_and_reset_downloadable( trans, import_results_tups )
|
||||
import_results_tups = import_util.create_repository_and_import_archive( trans.app,
|
||||
trans.request.host,
|
||||
trans.user,
|
||||
repository_status_info_dict,
|
||||
import_results_tups )
|
||||
import_util.check_status_and_reset_downloadable( trans.app, import_results_tups )
|
||||
basic_util.remove_dir( file_path )
|
||||
return trans.fill_template( '/webapps/tool_shed/repository/import_capsule_results.mako',
|
||||
export_info_dict=export_info_dict,
|
||||
@@ -2099,7 +2102,12 @@ class RepositoryController( BaseUIController, ratings_util.ItemRatings ):
|
||||
tool,
|
||||
[] )
|
||||
if invalid_file_tups:
|
||||
message = tool_util.generate_message_for_invalid_tools( trans, invalid_file_tups, repository, {}, as_html=True, displaying_invalid_tool=True )
|
||||
message = tool_util.generate_message_for_invalid_tools( trans.app,
|
||||
invalid_file_tups,
|
||||
repository,
|
||||
{},
|
||||
as_html=True,
|
||||
displaying_invalid_tool=True )
|
||||
elif error_message:
|
||||
message = error_message
|
||||
try:
|
||||
@@ -2459,7 +2467,10 @@ class RepositoryController( BaseUIController, ratings_util.ItemRatings ):
|
||||
if repository_metadata:
|
||||
metadata = repository_metadata.metadata
|
||||
role = repository.admin_role
|
||||
associations_dict = repository_maintenance_util.handle_role_associations( trans, role, repository, **kwd )
|
||||
associations_dict = repository_maintenance_util.handle_role_associations( trans.app,
|
||||
role,
|
||||
repository,
|
||||
**kwd )
|
||||
in_users = associations_dict.get( 'in_users', [] )
|
||||
out_users = associations_dict.get( 'out_users', [] )
|
||||
in_groups = associations_dict.get( 'in_groups', [] )
|
||||
@@ -2693,7 +2704,7 @@ class RepositoryController( BaseUIController, ratings_util.ItemRatings ):
|
||||
invalid_file_tups, metadata_dict = metadata_util.reset_all_metadata_on_repository_in_tool_shed( trans, id, **kwd )
|
||||
if invalid_file_tups:
|
||||
repository = suc.get_repository_in_tool_shed( trans.app, id )
|
||||
message = tool_util.generate_message_for_invalid_tools( trans, invalid_file_tups, repository, metadata_dict )
|
||||
message = tool_util.generate_message_for_invalid_tools( trans.app, invalid_file_tups, repository, metadata_dict )
|
||||
status = 'error'
|
||||
else:
|
||||
message = "All repository metadata has been reset. "
|
||||
@@ -2733,7 +2744,7 @@ class RepositoryController( BaseUIController, ratings_util.ItemRatings ):
|
||||
tip = repository.tip( trans.app )
|
||||
for selected_file in selected_files_to_delete:
|
||||
try:
|
||||
commands.remove( repo.ui, repo, selected_file, force=True )
|
||||
hg_util.remove_file( repo.ui, repo, selected_file, force=True )
|
||||
except Exception, e:
|
||||
log.debug( "Error removing the following file using the mercurial API:\n %s" % str( selected_file ) )
|
||||
log.debug( "The error was: %s" % str( e ))
|
||||
@@ -2759,19 +2770,27 @@ class RepositoryController( BaseUIController, ratings_util.ItemRatings ):
|
||||
# Commit the change set.
|
||||
if not commit_message:
|
||||
commit_message = 'Deleted selected files'
|
||||
commands.commit( repo.ui, repo, repo_dir, user=trans.user.username, message=commit_message )
|
||||
suc.handle_email_alerts( trans, repository )
|
||||
hg_util.commit_changeset( repo.ui,
|
||||
repo,
|
||||
full_path_to_changeset=repo_dir,
|
||||
username=trans.user.username,
|
||||
message=commit_message )
|
||||
suc.handle_email_alerts( trans.app, trans.request.host, repository )
|
||||
# Update the repository files for browsing.
|
||||
hg_util.update_repository( repo )
|
||||
# Get the new repository tip.
|
||||
if tip == repository.tip( trans.app ):
|
||||
message += 'No changes to repository. '
|
||||
kwd[ 'message' ] = message
|
||||
|
||||
else:
|
||||
message += 'The selected files were deleted from the repository. '
|
||||
kwd[ 'message' ] = message
|
||||
metadata_util.set_repository_metadata_due_to_new_tip( trans, repository, **kwd )
|
||||
status, error_message = metadata_util.set_repository_metadata_due_to_new_tip( trans.app,
|
||||
trans.request.host,
|
||||
trans.user,
|
||||
repository,
|
||||
**kwd )
|
||||
if error_message:
|
||||
message = error_message
|
||||
else:
|
||||
message += 'The selected files were deleted from the repository. '
|
||||
else:
|
||||
message = "Select at least 1 file to delete from the repository before clicking <b>Delete selected files</b>."
|
||||
status = "error"
|
||||
@@ -3061,13 +3080,13 @@ class RepositoryController( BaseUIController, ratings_util.ItemRatings ):
|
||||
status = kwd.get( 'status', 'done' )
|
||||
url = kwd.get( 'url', '' )
|
||||
if 'upload_capsule_button' in kwd:
|
||||
capsule_dict = import_util.upload_capsule( trans, **kwd )
|
||||
capsule_dict = import_util.upload_capsule( **kwd )
|
||||
status = capsule_dict.get( 'status', 'error' )
|
||||
if status == 'error':
|
||||
message = capsule_dict.get( 'error_message', '' )
|
||||
else:
|
||||
capsule_dict = import_util.extract_capsule_files( trans, **capsule_dict )
|
||||
capsule_dict = import_util.validate_capsule( trans, **capsule_dict )
|
||||
capsule_dict = import_util.extract_capsule_files( **capsule_dict )
|
||||
capsule_dict = import_util.validate_capsule( **capsule_dict )
|
||||
status = capsule_dict.get( 'status', 'error' )
|
||||
if status == 'ok':
|
||||
return trans.response.send_redirect( web.url_for( controller='repository',
|
||||
|
||||
@@ -167,7 +167,7 @@ class UploadController( BaseUIController ):
|
||||
# Inspect the contents of the file to see if changeset_revision values are missing and if so,
|
||||
# set them appropriately.
|
||||
altered, root_elem, error_message = \
|
||||
commit_util.handle_repository_dependencies_definition( trans,
|
||||
commit_util.handle_repository_dependencies_definition( trans.app,
|
||||
uploaded_file_name,
|
||||
unpopulate=False )
|
||||
if error_message:
|
||||
@@ -184,7 +184,7 @@ class UploadController( BaseUIController ):
|
||||
# Inspect the contents of the file to see if changeset_revision values are
|
||||
# missing and if so, set them appropriately.
|
||||
altered, root_elem, error_message = \
|
||||
commit_util.handle_tool_dependencies_definition( trans, uploaded_file_name )
|
||||
commit_util.handle_tool_dependencies_definition( trans.app, uploaded_file_name )
|
||||
if error_message:
|
||||
ok = False
|
||||
message = error_message
|
||||
@@ -200,15 +200,19 @@ class UploadController( BaseUIController ):
|
||||
if ok:
|
||||
# See if any admin users have chosen to receive email alerts when a repository is updated.
|
||||
# If so, check every uploaded file to ensure content is appropriate.
|
||||
check_contents = commit_util.check_file_contents_for_email_alerts( trans )
|
||||
check_contents = commit_util.check_file_contents_for_email_alerts( trans.app )
|
||||
if check_contents and os.path.isfile( full_path ):
|
||||
content_alert_str = commit_util.check_file_content_for_html_and_images( full_path )
|
||||
else:
|
||||
content_alert_str = ''
|
||||
commands.add( repo.ui, repo, full_path )
|
||||
hg_util.add_changeset( repo.ui, repo, full_path )
|
||||
# Convert from unicode to prevent "TypeError: array item must be char"
|
||||
full_path = full_path.encode( 'ascii', 'replace' )
|
||||
commands.commit( repo.ui, repo, full_path, user=trans.user.username, message=commit_message )
|
||||
hg_util.commit_changeset( repo.ui,
|
||||
repo,
|
||||
full_path_to_changeset=full_path,
|
||||
username=trans.user.username,
|
||||
message=commit_message )
|
||||
if full_path.endswith( 'tool_data_table_conf.xml.sample' ):
|
||||
# Handle the special case where a tool_data_table_conf.xml.sample file is being uploaded
|
||||
# by parsing the file and adding new entries to the in-memory trans.app.tool_data_tables
|
||||
@@ -218,7 +222,8 @@ class UploadController( BaseUIController ):
|
||||
message = '%s<br/>%s' % ( message, error_message )
|
||||
# See if the content of the change set was valid.
|
||||
admin_only = len( repository.downloadable_revisions ) != 1
|
||||
suc.handle_email_alerts( trans,
|
||||
suc.handle_email_alerts( trans.app,
|
||||
trans.request.host,
|
||||
repository,
|
||||
content_alert_str=content_alert_str,
|
||||
new_repo_alert=new_repo_alert,
|
||||
@@ -253,8 +258,15 @@ class UploadController( BaseUIController ):
|
||||
( len( files_to_remove ), upload_point )
|
||||
else:
|
||||
message += " %d files were removed from the repository root. " % len( files_to_remove )
|
||||
status, error_message = metadata_util.set_repository_metadata_due_to_new_tip( trans.app,
|
||||
trans.request.host,
|
||||
trans.user,
|
||||
repository,
|
||||
content_alert_str=content_alert_str,
|
||||
**kwd )
|
||||
if error_message:
|
||||
message = error_message
|
||||
kwd[ 'message' ] = message
|
||||
metadata_util.set_repository_metadata_due_to_new_tip( trans, repository, content_alert_str=content_alert_str, **kwd )
|
||||
if repository.metadata_revisions:
|
||||
# A repository's metadata revisions are order descending by update_time, so the zeroth revision
|
||||
# will be the tip just after an upload.
|
||||
@@ -355,7 +367,7 @@ class UploadController( BaseUIController ):
|
||||
# Inspect the contents of the file to see if changeset_revision values are missing and
|
||||
# if so, set them appropriately.
|
||||
altered, root_elem, error_message = \
|
||||
commit_util.handle_repository_dependencies_definition( trans,
|
||||
commit_util.handle_repository_dependencies_definition( trans.app,
|
||||
uploaded_file_name,
|
||||
unpopulate=False )
|
||||
if error_message:
|
||||
@@ -365,7 +377,7 @@ class UploadController( BaseUIController ):
|
||||
shutil.move( tmp_filename, uploaded_file_name )
|
||||
elif os.path.split( uploaded_file_name )[ -1 ] == rt_util.TOOL_DEPENDENCY_DEFINITION_FILENAME:
|
||||
# Inspect the contents of the file to see if changeset_revision values are missing and if so, set them appropriately.
|
||||
altered, root_elem, error_message = commit_util.handle_tool_dependencies_definition( trans, uploaded_file_name )
|
||||
altered, root_elem, error_message = commit_util.handle_tool_dependencies_definition( trans.app, uploaded_file_name )
|
||||
if error_message:
|
||||
return False, error_message, [], '', [], []
|
||||
if altered:
|
||||
@@ -382,8 +394,17 @@ class UploadController( BaseUIController ):
|
||||
os.remove( repo_path )
|
||||
shutil.move( os.path.join( uploaded_directory, relative_path ), repo_path )
|
||||
filenames_in_archive.append( relative_path )
|
||||
return commit_util.handle_directory_changes( trans, repository, full_path, filenames_in_archive, remove_repo_files_not_in_tar,
|
||||
new_repo_alert, commit_message, undesirable_dirs_removed, undesirable_files_removed )
|
||||
return commit_util.handle_directory_changes( trans.app,
|
||||
trans.request.host,
|
||||
trans.user.username,
|
||||
repository,
|
||||
full_path,
|
||||
filenames_in_archive,
|
||||
remove_repo_files_not_in_tar,
|
||||
new_repo_alert,
|
||||
commit_message,
|
||||
undesirable_dirs_removed,
|
||||
undesirable_files_removed )
|
||||
|
||||
def upload_tar( self, trans, repository, tar, uploaded_file, upload_point, remove_repo_files_not_in_tar, commit_message, new_repo_alert ):
|
||||
# Upload a tar archive of files.
|
||||
@@ -422,7 +443,7 @@ class UploadController( BaseUIController ):
|
||||
uploaded_file_name = os.path.join( full_path, filename )
|
||||
if os.path.split( uploaded_file_name )[ -1 ] == rt_util.REPOSITORY_DEPENDENCY_DEFINITION_FILENAME:
|
||||
# Inspect the contents of the file to see if changeset_revision values are missing and if so, set them appropriately.
|
||||
altered, root_elem, error_message = commit_util.handle_repository_dependencies_definition( trans,
|
||||
altered, root_elem, error_message = commit_util.handle_repository_dependencies_definition( trans.app,
|
||||
uploaded_file_name,
|
||||
unpopulate=False )
|
||||
if error_message:
|
||||
@@ -432,13 +453,15 @@ class UploadController( BaseUIController ):
|
||||
shutil.move( tmp_filename, uploaded_file_name )
|
||||
elif os.path.split( uploaded_file_name )[ -1 ] == rt_util.TOOL_DEPENDENCY_DEFINITION_FILENAME:
|
||||
# Inspect the contents of the file to see if changeset_revision values are missing and if so, set them appropriately.
|
||||
altered, root_elem, error_message = commit_util.handle_tool_dependencies_definition( trans, uploaded_file_name )
|
||||
altered, root_elem, error_message = commit_util.handle_tool_dependencies_definition( trans.app, uploaded_file_name )
|
||||
if error_message:
|
||||
return False, error_message, [], '', [], []
|
||||
if altered:
|
||||
tmp_filename = xml_util.create_and_write_tmp_file( root_elem )
|
||||
shutil.move( tmp_filename, uploaded_file_name )
|
||||
return commit_util.handle_directory_changes( trans,
|
||||
return commit_util.handle_directory_changes( trans.app,
|
||||
trans.request.host,
|
||||
trans.user.username,
|
||||
repository,
|
||||
full_path,
|
||||
filenames_in_archive,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Middle-ware for handling hg authentication for users pushing change sets to local repositories."""
|
||||
import os
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import sqlalchemy
|
||||
import sys
|
||||
import tempfile
|
||||
@@ -9,11 +10,9 @@ from paste.httpheaders import AUTH_TYPE
|
||||
from paste.httpheaders import REMOTE_USER
|
||||
|
||||
from galaxy.util import asbool
|
||||
from galaxy.util import json
|
||||
from galaxy.webapps.tool_shed import model
|
||||
from galaxy.util.hash_util import new_secure_hash
|
||||
import tool_shed.util.shed_util_common as suc
|
||||
from tool_shed.util import commit_util
|
||||
from tool_shed.util import hg_util
|
||||
import tool_shed.repository_types.util as rt_util
|
||||
|
||||
from galaxy import eggs
|
||||
@@ -104,7 +103,7 @@ class Hg( object ):
|
||||
fh.write( chunk )
|
||||
fh.close()
|
||||
fh = open( tmp_filename, 'rb' )
|
||||
changeset_groups = json.from_json_string( commit_util.bundle_to_json( fh ) )
|
||||
changeset_groups = json.loads( hg_util.bundle_to_json( fh ) )
|
||||
fh.close()
|
||||
try:
|
||||
os.unlink( tmp_filename )
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
import cStringIO
|
||||
import gzip
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import pkg_resources
|
||||
import shutil
|
||||
import struct
|
||||
import tempfile
|
||||
from galaxy.datatypes import checkers
|
||||
from galaxy.util import asbool
|
||||
from galaxy.util import json
|
||||
from galaxy.util.odict import odict
|
||||
from galaxy.web import url_for
|
||||
import tool_shed.util.shed_util_common as suc
|
||||
@@ -18,24 +15,11 @@ from tool_shed.util import tool_util
|
||||
from tool_shed.util import xml_util
|
||||
import tool_shed.repository_types.util as rt_util
|
||||
|
||||
from galaxy import eggs
|
||||
eggs.require( 'mercurial' )
|
||||
from mercurial import commands
|
||||
from mercurial.changegroup import readbundle
|
||||
from mercurial.changegroup import readexactly
|
||||
|
||||
log = logging.getLogger( __name__ )
|
||||
|
||||
UNDESIRABLE_DIRS = [ '.hg', '.svn', '.git', '.cvs' ]
|
||||
UNDESIRABLE_FILES = [ '.hg_archival.txt', 'hgrc', '.DS_Store' ]
|
||||
|
||||
def bundle_to_json( fh ):
|
||||
"""Convert the received HG10xx data stream (a mercurial 1.0 bundle created using hg push from the command line) to a json object."""
|
||||
# See http://www.wstein.org/home/wstein/www/home/was/patches/hg_json
|
||||
hg_unbundle10_obj = readbundle( fh, None )
|
||||
groups = [ group for group in unpack_groups( hg_unbundle10_obj ) ]
|
||||
return json.to_json_string( groups, indent=4 )
|
||||
|
||||
def check_archive( repository, archive ):
|
||||
for member in archive.getmembers():
|
||||
# Allow regular files and directories only
|
||||
@@ -62,15 +46,16 @@ def check_archive( repository, archive ):
|
||||
return False, message
|
||||
return True, ''
|
||||
|
||||
def check_file_contents_for_email_alerts( trans ):
|
||||
def check_file_contents_for_email_alerts( app ):
|
||||
"""
|
||||
See if any admin users have chosen to receive email alerts when a repository is updated. If so, the file contents of the update must be
|
||||
checked for inappropriate content.
|
||||
"""
|
||||
admin_users = trans.app.config.get( "admin_users", "" ).split( "," )
|
||||
for repository in trans.sa_session.query( trans.model.Repository ) \
|
||||
.filter( trans.model.Repository.table.c.email_alerts != None ):
|
||||
email_alerts = json.from_json_string( repository.email_alerts )
|
||||
sa_session = app.model.context.current
|
||||
admin_users = app.config.get( "admin_users", "" ).split( "," )
|
||||
for repository in sa_session.query( app.model.Repository ) \
|
||||
.filter( app.model.Repository.table.c.email_alerts != None ):
|
||||
email_alerts = json.loads( repository.email_alerts )
|
||||
for user_email in email_alerts:
|
||||
if user_email in admin_users:
|
||||
return True
|
||||
@@ -140,14 +125,14 @@ def handle_bz2( repository, uploaded_file_name ):
|
||||
bzipped_file.close()
|
||||
shutil.move( uncompressed, uploaded_file_name )
|
||||
|
||||
def handle_complex_repository_dependency_elem( trans, elem, sub_elem_index, sub_elem, sub_elem_altered, altered, unpopulate=False ):
|
||||
def handle_complex_repository_dependency_elem( app, elem, sub_elem_index, sub_elem, sub_elem_altered, altered, unpopulate=False ):
|
||||
"""
|
||||
Populate or unpopulate the toolshed and changeset_revision attributes of a <repository> tag that defines
|
||||
a complex repository dependency.
|
||||
"""
|
||||
# The received sub_elem looks something like the following:
|
||||
# <repository name="package_eigen_2_0" owner="test" prior_installation_required="True" />
|
||||
revised, repository_elem, error_message = handle_repository_dependency_elem( trans, sub_elem, unpopulate=unpopulate )
|
||||
revised, repository_elem, error_message = handle_repository_dependency_elem( app, sub_elem, unpopulate=unpopulate )
|
||||
if error_message:
|
||||
error_message = 'The tool_dependencies.xml file contains an invalid <repository> tag. %s' % error_message
|
||||
if revised:
|
||||
@@ -157,13 +142,13 @@ def handle_complex_repository_dependency_elem( trans, elem, sub_elem_index, sub_
|
||||
altered = True
|
||||
return altered, sub_elem_altered, elem, error_message
|
||||
|
||||
def handle_directory_changes( trans, repository, full_path, filenames_in_archive, remove_repo_files_not_in_tar, new_repo_alert,
|
||||
commit_message, undesirable_dirs_removed, undesirable_files_removed ):
|
||||
repo = hg_util.get_repo_for_repository( trans.app, repository=repository, repo_path=None, create=False )
|
||||
def handle_directory_changes( app, host, username, repository, full_path, filenames_in_archive, remove_repo_files_not_in_tar,
|
||||
new_repo_alert, commit_message, undesirable_dirs_removed, undesirable_files_removed ):
|
||||
repo = hg_util.get_repo_for_repository( app, repository=repository, repo_path=None, create=False )
|
||||
content_alert_str = ''
|
||||
files_to_remove = []
|
||||
filenames_in_archive = [ os.path.join( full_path, name ) for name in filenames_in_archive ]
|
||||
if remove_repo_files_not_in_tar and not repository.is_new( trans.app ):
|
||||
if remove_repo_files_not_in_tar and not repository.is_new( app ):
|
||||
# We have a repository that is not new (it contains files), so discover those files that are in the
|
||||
# repository, but not in the uploaded archive.
|
||||
for root, dirs, files in os.walk( full_path ):
|
||||
@@ -181,9 +166,10 @@ def handle_directory_changes( trans, repository, full_path, filenames_in_archive
|
||||
if full_name not in filenames_in_archive:
|
||||
files_to_remove.append( full_name )
|
||||
for repo_file in files_to_remove:
|
||||
# Remove files in the repository (relative to the upload point) that are not in the uploaded archive.
|
||||
# Remove files in the repository (relative to the upload point) that are not in
|
||||
# the uploaded archive.
|
||||
try:
|
||||
commands.remove( repo.ui, repo, repo_file, force=True )
|
||||
hg_util.remove_file( repo.ui, repo, repo_file, force=True )
|
||||
except Exception, e:
|
||||
log.debug( "Error removing files using the mercurial API, so trying a different approach, the error was: %s" % str( e ))
|
||||
relative_selected_file = repo_file.split( 'repo_%d' % repository.id )[1].lstrip( '/' )
|
||||
@@ -204,23 +190,33 @@ def handle_directory_changes( trans, repository, full_path, filenames_in_archive
|
||||
except OSError, e:
|
||||
# The directory is not empty.
|
||||
pass
|
||||
# See if any admin users have chosen to receive email alerts when a repository is updated. If so, check every uploaded file to ensure
|
||||
# content is appropriate.
|
||||
check_contents = check_file_contents_for_email_alerts( trans )
|
||||
# See if any admin users have chosen to receive email alerts when a repository is updated.
|
||||
# If so, check every uploaded file to ensure content is appropriate.
|
||||
check_contents = check_file_contents_for_email_alerts( app )
|
||||
for filename_in_archive in filenames_in_archive:
|
||||
# Check file content to ensure it is appropriate.
|
||||
if check_contents and os.path.isfile( filename_in_archive ):
|
||||
content_alert_str += check_file_content_for_html_and_images( filename_in_archive )
|
||||
commands.add( repo.ui, repo, filename_in_archive )
|
||||
hg_util.add_changeset( repo.ui, repo, filename_in_archive )
|
||||
if filename_in_archive.endswith( 'tool_data_table_conf.xml.sample' ):
|
||||
# Handle the special case where a tool_data_table_conf.xml.sample file is being uploaded by parsing the file and adding new entries
|
||||
# to the in-memory trans.app.tool_data_tables dictionary.
|
||||
error, message = tool_util.handle_sample_tool_data_table_conf_file( trans.app, filename_in_archive )
|
||||
# Handle the special case where a tool_data_table_conf.xml.sample file is being uploaded
|
||||
# by parsing the file and adding new entries to the in-memory app.tool_data_tables
|
||||
# dictionary.
|
||||
error, message = tool_util.handle_sample_tool_data_table_conf_file( app, filename_in_archive )
|
||||
if error:
|
||||
return False, message, files_to_remove, content_alert_str, undesirable_dirs_removed, undesirable_files_removed
|
||||
commands.commit( repo.ui, repo, full_path, user=trans.user.username, message=commit_message )
|
||||
hg_util.commit_changeset( repo.ui,
|
||||
repo,
|
||||
full_path_to_changeset=full_path,
|
||||
username=username,
|
||||
message=commit_message )
|
||||
admin_only = len( repository.downloadable_revisions ) != 1
|
||||
suc.handle_email_alerts( trans, repository, content_alert_str=content_alert_str, new_repo_alert=new_repo_alert, admin_only=admin_only )
|
||||
suc.handle_email_alerts( app,
|
||||
host,
|
||||
repository,
|
||||
content_alert_str=content_alert_str,
|
||||
new_repo_alert=new_repo_alert,
|
||||
admin_only=admin_only )
|
||||
return True, '', files_to_remove, content_alert_str, undesirable_dirs_removed, undesirable_files_removed
|
||||
|
||||
def handle_missing_repository_attribute( elem ):
|
||||
@@ -253,7 +249,7 @@ def handle_gzip( repository, uploaded_file_name ):
|
||||
gzipped_file.close()
|
||||
shutil.move( uncompressed, uploaded_file_name )
|
||||
|
||||
def handle_repository_dependencies_definition( trans, repository_dependencies_config, unpopulate=False ):
|
||||
def handle_repository_dependencies_definition( app, repository_dependencies_config, unpopulate=False ):
|
||||
"""
|
||||
Populate or unpopulate the toolshed and changeset_revision attributes of a <repository> tag. Populating will occur when a
|
||||
dependency definition file is being uploaded to the repository, while depopulating will occur when the repository is being
|
||||
@@ -269,7 +265,7 @@ def handle_repository_dependencies_definition( trans, repository_dependencies_co
|
||||
for index, elem in enumerate( root ):
|
||||
if elem.tag == 'repository':
|
||||
# <repository name="molecule_datatypes" owner="test" changeset_revision="1a070566e9c6" />
|
||||
revised, elem, error_message = handle_repository_dependency_elem( trans, elem, unpopulate=unpopulate )
|
||||
revised, elem, error_message = handle_repository_dependency_elem( app, elem, unpopulate=unpopulate )
|
||||
if error_message:
|
||||
error_message = 'The repository_dependencies.xml file contains an invalid <repository> tag. %s' % error_message
|
||||
return False, None, error_message
|
||||
@@ -280,7 +276,7 @@ def handle_repository_dependencies_definition( trans, repository_dependencies_co
|
||||
return altered, root, error_message
|
||||
return False, None, error_message
|
||||
|
||||
def handle_repository_dependency_elem( trans, elem, unpopulate=False ):
|
||||
def handle_repository_dependency_elem( app, elem, unpopulate=False ):
|
||||
"""Populate or unpopulate repository tags."""
|
||||
# <repository name="molecule_datatypes" owner="test" changeset_revision="1a070566e9c6" />
|
||||
# <repository changeset_revision="xxx" name="package_xorg_macros_1_17_1" owner="test" toolshed="yyy">
|
||||
@@ -342,10 +338,10 @@ def handle_repository_dependency_elem( trans, elem, unpopulate=False ):
|
||||
# Populate the changeset_revision attribute with the latest installable metadata revision for the defined repository.
|
||||
# We use the latest installable revision instead of the latest metadata revision to ensure that the contents of the
|
||||
# revision are valid.
|
||||
repository = suc.get_repository_by_name_and_owner( trans.app, name, owner )
|
||||
repository = suc.get_repository_by_name_and_owner( app, name, owner )
|
||||
if repository:
|
||||
repo = hg_util.get_repo_for_repository( trans.app, repository=repository, repo_path=None, create=False )
|
||||
lastest_installable_changeset_revision = suc.get_latest_downloadable_changeset_revision( trans.app, repository, repo )
|
||||
repo = hg_util.get_repo_for_repository( app, repository=repository, repo_path=None, create=False )
|
||||
lastest_installable_changeset_revision = suc.get_latest_downloadable_changeset_revision( app, repository, repo )
|
||||
if lastest_installable_changeset_revision != hg_util.INITIAL_CHANGELOG_HASH:
|
||||
elem.attrib[ 'changeset_revision' ] = lastest_installable_changeset_revision
|
||||
revised = True
|
||||
@@ -356,7 +352,7 @@ def handle_repository_dependency_elem( trans, elem, unpopulate=False ):
|
||||
error_message = 'Unable to locate repository with name %s and owner %s. ' % ( str( name ), str( owner ) )
|
||||
return revised, elem, error_message
|
||||
|
||||
def handle_repository_dependency_sub_elem( trans, package_altered, altered, actions_elem, action_index, action_elem, unpopulate=False ):
|
||||
def handle_repository_dependency_sub_elem( app, package_altered, altered, actions_elem, action_index, action_elem, unpopulate=False ):
|
||||
"""
|
||||
Populate or unpopulate the toolshed and changeset_revision attributes for each of the following tag sets.
|
||||
<action type="set_environment_for_install">
|
||||
@@ -367,7 +363,7 @@ def handle_repository_dependency_sub_elem( trans, package_altered, altered, acti
|
||||
for repo_index, repo_elem in enumerate( action_elem ):
|
||||
# Make sure to skip comments and tags that are not <repository>.
|
||||
if repo_elem.tag == 'repository':
|
||||
revised, repository_elem, message = handle_repository_dependency_elem( trans, repo_elem, unpopulate=unpopulate )
|
||||
revised, repository_elem, message = handle_repository_dependency_elem( app, repo_elem, unpopulate=unpopulate )
|
||||
if message:
|
||||
error_message += 'The tool_dependencies.xml file contains an invalid <repository> tag. %s' % message
|
||||
if revised:
|
||||
@@ -379,7 +375,7 @@ def handle_repository_dependency_sub_elem( trans, package_altered, altered, acti
|
||||
actions_elem[ action_index ] = action_elem
|
||||
return package_altered, altered, actions_elem, error_message
|
||||
|
||||
def handle_tool_dependencies_definition( trans, tool_dependencies_config, unpopulate=False ):
|
||||
def handle_tool_dependencies_definition( app, tool_dependencies_config, unpopulate=False ):
|
||||
"""
|
||||
Populate or unpopulate the tooshed and changeset_revision attributes of each <repository>
|
||||
tag defined within a tool_dependencies.xml file.
|
||||
@@ -401,7 +397,7 @@ def handle_tool_dependencies_definition( trans, tool_dependencies_config, unpopu
|
||||
if package_elem.tag == 'repository':
|
||||
# We have a complex repository dependency.
|
||||
altered, package_altered, root_elem, message = \
|
||||
handle_complex_repository_dependency_elem( trans,
|
||||
handle_complex_repository_dependency_elem( app,
|
||||
root_elem,
|
||||
package_index,
|
||||
package_elem,
|
||||
@@ -440,7 +436,7 @@ def handle_tool_dependencies_definition( trans, tool_dependencies_config, unpopu
|
||||
if last_actions_elem_package_elem.tag == 'repository':
|
||||
# We have a complex repository dependency.
|
||||
altered, last_actions_package_altered, last_actions_elem, message = \
|
||||
handle_complex_repository_dependency_elem( trans,
|
||||
handle_complex_repository_dependency_elem( app,
|
||||
last_actions_elem,
|
||||
last_actions_elem_package_index,
|
||||
last_actions_elem_package_elem,
|
||||
@@ -456,7 +452,7 @@ def handle_tool_dependencies_definition( trans, tool_dependencies_config, unpopu
|
||||
# Inspect the sub elements of last_actions_elem to locate all <repository> tags and
|
||||
# populate them with toolshed and changeset_revision attributes if necessary.
|
||||
last_actions_package_altered, altered, last_actions_elem, message = \
|
||||
handle_repository_dependency_sub_elem( trans,
|
||||
handle_repository_dependency_sub_elem( app,
|
||||
last_actions_package_altered,
|
||||
altered,
|
||||
actions_group_elem,
|
||||
@@ -468,23 +464,25 @@ def handle_tool_dependencies_definition( trans, tool_dependencies_config, unpopu
|
||||
elif actions_elem.tag == 'actions':
|
||||
# We are not in an <actions_group> tag set, so we must be in an <actions> tag set.
|
||||
for action_index, action_elem in enumerate( actions_elem ):
|
||||
# Inspect the sub elements of last_actions_elem to locate all <repository> tags and populate them with
|
||||
# toolshed and changeset_revision attributes if necessary.
|
||||
package_altered, altered, actions_elem, message = handle_repository_dependency_sub_elem( trans,
|
||||
package_altered,
|
||||
altered,
|
||||
actions_elem,
|
||||
action_index,
|
||||
action_elem,
|
||||
unpopulate=unpopulate )
|
||||
# Inspect the sub elements of last_actions_elem to locate all <repository> tags
|
||||
# and populate them with toolshed and changeset_revision attributes if necessary.
|
||||
package_altered, altered, actions_elem, message = \
|
||||
handle_repository_dependency_sub_elem( app,
|
||||
package_altered,
|
||||
altered,
|
||||
actions_elem,
|
||||
action_index,
|
||||
action_elem,
|
||||
unpopulate=unpopulate )
|
||||
if message:
|
||||
error_message += message
|
||||
else:
|
||||
package_name = root_elem.get( 'name', '' )
|
||||
package_version = root_elem.get( 'version', '' )
|
||||
error_message += 'Version %s of the %s package cannot be installed because ' % ( str( package_version ), str( package_name ) )
|
||||
error_message += 'the recipe for installing the package is missing either an <actions> tag set or an <actions_group> '
|
||||
error_message += 'tag set.'
|
||||
error_message += 'Version %s of the %s package cannot be installed because ' % \
|
||||
( str( package_version ), str( package_name ) )
|
||||
error_message += 'the recipe for installing the package is missing either an '
|
||||
error_message += '<actions> tag set or an <actions_group> tag set.'
|
||||
if package_altered:
|
||||
package_elem[ actions_index ] = actions_elem
|
||||
if package_altered:
|
||||
@@ -534,62 +532,3 @@ def uncompress( repository, uploaded_file_name, uploaded_file_filename, isgzip=F
|
||||
if isbz2:
|
||||
handle_bz2( repository, uploaded_file_name )
|
||||
return uploaded_file_filename.rstrip( '.bz2' )
|
||||
|
||||
def unpack_chunks( hg_unbundle10_obj ):
|
||||
"""
|
||||
This method provides a generator of parsed chunks of a "group" in a mercurial unbundle10 object which
|
||||
is created when a changeset that is pushed to a Tool Shed repository using hg push from the command line
|
||||
is read using readbundle.
|
||||
"""
|
||||
while True:
|
||||
length, = struct.unpack( '>l', readexactly( hg_unbundle10_obj, 4 ) )
|
||||
if length <= 4:
|
||||
# We found a "null chunk", which ends the group.
|
||||
break
|
||||
if length < 84:
|
||||
raise Exception( "negative data length" )
|
||||
node, p1, p2, cs = struct.unpack( '20s20s20s20s', readexactly( hg_unbundle10_obj, 80 ) )
|
||||
yield { 'node': node.encode( 'hex' ),
|
||||
'p1': p1.encode( 'hex' ),
|
||||
'p2': p2.encode( 'hex' ),
|
||||
'cs': cs.encode( 'hex' ),
|
||||
'data': [ patch for patch in unpack_patches( hg_unbundle10_obj, length - 84 ) ] }
|
||||
|
||||
def unpack_groups( hg_unbundle10_obj ):
|
||||
"""
|
||||
This method provides a generator of parsed groups from a mercurial unbundle10 object which is
|
||||
created when a changeset that is pushed to a Tool Shed repository using hg push from the command
|
||||
line is read using readbundle.
|
||||
"""
|
||||
# Process the changelog group.
|
||||
yield [ chunk for chunk in unpack_chunks( hg_unbundle10_obj ) ]
|
||||
# Process the manifest group.
|
||||
yield [ chunk for chunk in unpack_chunks( hg_unbundle10_obj ) ]
|
||||
while True:
|
||||
length, = struct.unpack( '>l', readexactly( hg_unbundle10_obj, 4 ) )
|
||||
if length <= 4:
|
||||
# We found a "null meta chunk", which ends the changegroup.
|
||||
break
|
||||
filename = readexactly( hg_unbundle10_obj, length-4 ).encode( 'string_escape' )
|
||||
# Process the file group.
|
||||
yield ( filename, [ chunk for chunk in unpack_chunks( hg_unbundle10_obj ) ] )
|
||||
|
||||
def unpack_patches( hg_unbundle10_obj, remaining ):
|
||||
"""
|
||||
This method provides a generator of patches from the data field in a chunk. As there is no delimiter
|
||||
for this data field, a length argument is required.
|
||||
"""
|
||||
while remaining >= 12:
|
||||
start, end, blocklen = struct.unpack( '>lll', readexactly( hg_unbundle10_obj, 12 ) )
|
||||
remaining -= 12
|
||||
if blocklen > remaining:
|
||||
raise Exception( "unexpected end of patch stream" )
|
||||
block = readexactly( hg_unbundle10_obj, blocklen )
|
||||
remaining -= blocklen
|
||||
yield { 'start': start,
|
||||
'end': end,
|
||||
'blocklen': blocklen,
|
||||
'block': block.encode( 'string_escape' ) }
|
||||
if remaining > 0:
|
||||
print remaining
|
||||
raise Exception( "unexpected end of patch stream" )
|
||||
|
||||
@@ -151,7 +151,7 @@ def generate_repository_archive( trans, work_dir, tool_shed_url, repository, cha
|
||||
if name == rt_util.REPOSITORY_DEPENDENCY_DEFINITION_FILENAME:
|
||||
# Eliminate the toolshed, and changeset_revision attributes from all <repository> tags.
|
||||
altered, root_elem, error_message = \
|
||||
commit_util.handle_repository_dependencies_definition( trans, full_path, unpopulate=True )
|
||||
commit_util.handle_repository_dependencies_definition( trans.app, full_path, unpopulate=True )
|
||||
if error_message:
|
||||
return None, error_message
|
||||
if altered:
|
||||
@@ -160,7 +160,7 @@ def generate_repository_archive( trans, work_dir, tool_shed_url, repository, cha
|
||||
elif name == rt_util.TOOL_DEPENDENCY_DEFINITION_FILENAME:
|
||||
# Eliminate the toolshed, and changeset_revision attributes from all <repository> tags.
|
||||
altered, root_elem, error_message = \
|
||||
commit_util.handle_tool_dependencies_definition( trans, full_path, unpopulate=True )
|
||||
commit_util.handle_tool_dependencies_definition( trans.app, full_path, unpopulate=True )
|
||||
if error_message:
|
||||
return None, error_message
|
||||
if altered:
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import struct
|
||||
|
||||
from datetime import datetime
|
||||
from time import gmtime
|
||||
@@ -14,6 +16,8 @@ from mercurial import cmdutil
|
||||
from mercurial import commands
|
||||
from mercurial import hg
|
||||
from mercurial import ui
|
||||
from mercurial.changegroup import readbundle
|
||||
from mercurial.changegroup import readexactly
|
||||
|
||||
from tool_shed.util import basic_util
|
||||
|
||||
@@ -21,6 +25,19 @@ log = logging.getLogger( __name__ )
|
||||
|
||||
INITIAL_CHANGELOG_HASH = '000000000000'
|
||||
|
||||
def add_changeset( repo_ui, repo, path_to_filename_in_archive ):
|
||||
commands.add( repo_ui, repo, path_to_filename_in_archive )
|
||||
|
||||
def bundle_to_json( fh ):
|
||||
"""
|
||||
Convert the received HG10xx data stream (a mercurial 1.0 bundle created using hg push from the
|
||||
command line) to a json object.
|
||||
"""
|
||||
# See http://www.wstein.org/home/wstein/www/home/was/patches/hg_json
|
||||
hg_unbundle10_obj = readbundle( fh, None )
|
||||
groups = [ group for group in unpack_groups( hg_unbundle10_obj ) ]
|
||||
return json.dumps( groups, indent=4 )
|
||||
|
||||
def clone_repository( repository_clone_url, repository_file_dir, ctx_rev ):
|
||||
"""
|
||||
Clone the repository up to the specified changeset_revision. No subsequent revisions will be
|
||||
@@ -39,6 +56,9 @@ def clone_repository( repository_clone_url, repository_file_dir, ctx_rev ):
|
||||
log.debug( error_message )
|
||||
return False, error_message
|
||||
|
||||
def commit_changeset( repo_ui, repo, full_path_to_changeset, username, message ):
|
||||
commands.commit( repo_ui, repo, full_path_to_changeset, user=username, message=message )
|
||||
|
||||
def copy_file_from_manifest( repo, ctx, filename, dir ):
|
||||
"""
|
||||
Copy the latest version of the file named filename from the repository manifest to the directory
|
||||
@@ -268,6 +288,9 @@ def pull_repository( repo, repository_clone_url, ctx_rev ):
|
||||
"""Pull changes from a remote repository to a local one."""
|
||||
commands.pull( get_configured_ui(), repo, source=repository_clone_url, rev=[ ctx_rev ] )
|
||||
|
||||
def remove_file( repo_ui, repo, selected_file, force=True ):
|
||||
commands.remove( repo_ui, repo, selected_file, force=force )
|
||||
|
||||
def reversed_lower_upper_bounded_changelog( repo, excluded_lower_bounds_changeset_revision, included_upper_bounds_changeset_revision ):
|
||||
"""
|
||||
Return a reversed list of changesets in the repository changelog after the excluded_lower_bounds_changeset_revision,
|
||||
@@ -300,6 +323,65 @@ def reversed_upper_bounded_changelog( repo, included_upper_bounds_changeset_revi
|
||||
"""
|
||||
return reversed_lower_upper_bounded_changelog( repo, INITIAL_CHANGELOG_HASH, included_upper_bounds_changeset_revision )
|
||||
|
||||
def unpack_chunks( hg_unbundle10_obj ):
|
||||
"""
|
||||
This method provides a generator of parsed chunks of a "group" in a mercurial unbundle10 object which
|
||||
is created when a changeset that is pushed to a Tool Shed repository using hg push from the command line
|
||||
is read using readbundle.
|
||||
"""
|
||||
while True:
|
||||
length, = struct.unpack( '>l', readexactly( hg_unbundle10_obj, 4 ) )
|
||||
if length <= 4:
|
||||
# We found a "null chunk", which ends the group.
|
||||
break
|
||||
if length < 84:
|
||||
raise Exception( "negative data length" )
|
||||
node, p1, p2, cs = struct.unpack( '20s20s20s20s', readexactly( hg_unbundle10_obj, 80 ) )
|
||||
yield { 'node': node.encode( 'hex' ),
|
||||
'p1': p1.encode( 'hex' ),
|
||||
'p2': p2.encode( 'hex' ),
|
||||
'cs': cs.encode( 'hex' ),
|
||||
'data': [ patch for patch in unpack_patches( hg_unbundle10_obj, length - 84 ) ] }
|
||||
|
||||
def unpack_groups( hg_unbundle10_obj ):
|
||||
"""
|
||||
This method provides a generator of parsed groups from a mercurial unbundle10 object which is
|
||||
created when a changeset that is pushed to a Tool Shed repository using hg push from the command
|
||||
line is read using readbundle.
|
||||
"""
|
||||
# Process the changelog group.
|
||||
yield [ chunk for chunk in unpack_chunks( hg_unbundle10_obj ) ]
|
||||
# Process the manifest group.
|
||||
yield [ chunk for chunk in unpack_chunks( hg_unbundle10_obj ) ]
|
||||
while True:
|
||||
length, = struct.unpack( '>l', readexactly( hg_unbundle10_obj, 4 ) )
|
||||
if length <= 4:
|
||||
# We found a "null meta chunk", which ends the changegroup.
|
||||
break
|
||||
filename = readexactly( hg_unbundle10_obj, length-4 ).encode( 'string_escape' )
|
||||
# Process the file group.
|
||||
yield ( filename, [ chunk for chunk in unpack_chunks( hg_unbundle10_obj ) ] )
|
||||
|
||||
def unpack_patches( hg_unbundle10_obj, remaining ):
|
||||
"""
|
||||
This method provides a generator of patches from the data field in a chunk. As there is no delimiter
|
||||
for this data field, a length argument is required.
|
||||
"""
|
||||
while remaining >= 12:
|
||||
start, end, blocklen = struct.unpack( '>lll', readexactly( hg_unbundle10_obj, 12 ) )
|
||||
remaining -= 12
|
||||
if blocklen > remaining:
|
||||
raise Exception( "unexpected end of patch stream" )
|
||||
block = readexactly( hg_unbundle10_obj, blocklen )
|
||||
remaining -= blocklen
|
||||
yield { 'start': start,
|
||||
'end': end,
|
||||
'blocklen': blocklen,
|
||||
'block': block.encode( 'string_escape' ) }
|
||||
if remaining > 0:
|
||||
print remaining
|
||||
raise Exception( "unexpected end of patch stream" )
|
||||
|
||||
def update_repository( repo, ctx_rev=None ):
|
||||
"""
|
||||
Update the cloned repository to changeset_revision. It is critical that the installed repository is updated to the desired
|
||||
|
||||
@@ -5,50 +5,117 @@ import tarfile
|
||||
import tempfile
|
||||
import urllib
|
||||
from galaxy import util
|
||||
from galaxy.datatypes import checkers
|
||||
from tool_shed.util import commit_util
|
||||
from tool_shed.util import encoding_util
|
||||
from tool_shed.util import hg_util
|
||||
from tool_shed.util import metadata_util
|
||||
from tool_shed.util import repository_maintenance_util
|
||||
from tool_shed.util import xml_util
|
||||
import tool_shed.util.shed_util_common as suc
|
||||
import tool_shed.repository_types.util as rt_util
|
||||
|
||||
log = logging.getLogger( __name__ )
|
||||
|
||||
def check_status_and_reset_downloadable( trans, import_results_tups ):
|
||||
def check_status_and_reset_downloadable( app, import_results_tups ):
|
||||
"""Check the status of each imported repository and set downloadable to False if errors."""
|
||||
sa_session = app.model.context.current
|
||||
flush = False
|
||||
for import_results_tup in import_results_tups:
|
||||
ok, name_owner, message = import_results_tup
|
||||
name, owner = name_owner
|
||||
if not ok:
|
||||
repository = suc.get_repository_by_name_and_owner( trans.app, name, owner )
|
||||
repository = suc.get_repository_by_name_and_owner( app, name, owner )
|
||||
if repository is not None:
|
||||
# Do not allow the repository to be automatically installed if population resulted in errors.
|
||||
tip_changeset_revision = repository.tip( trans.app )
|
||||
repository_metadata = suc.get_repository_metadata_by_changeset_revision( trans.app,
|
||||
trans.security.encode_id( repository.id ),
|
||||
tip_changeset_revision = repository.tip( app )
|
||||
repository_metadata = suc.get_repository_metadata_by_changeset_revision( app,
|
||||
app.security.encode_id( repository.id ),
|
||||
tip_changeset_revision )
|
||||
if repository_metadata:
|
||||
if repository_metadata.downloadable:
|
||||
repository_metadata.downloadable = False
|
||||
trans.sa_session.add( repository_metadata )
|
||||
sa_session.add( repository_metadata )
|
||||
if not flush:
|
||||
flush = True
|
||||
# Do not allow dependent repository revisions to be automatically installed if population
|
||||
# resulted in errors.
|
||||
dependent_downloadable_revisions = suc.get_dependent_downloadable_revisions( trans.app, repository_metadata )
|
||||
dependent_downloadable_revisions = suc.get_dependent_downloadable_revisions( app, repository_metadata )
|
||||
for dependent_downloadable_revision in dependent_downloadable_revisions:
|
||||
if dependent_downloadable_revision.downloadable:
|
||||
dependent_downloadable_revision.downloadable = False
|
||||
trans.sa_session.add( dependent_downloadable_revision )
|
||||
sa_session.add( dependent_downloadable_revision )
|
||||
if not flush:
|
||||
flush = True
|
||||
if flush:
|
||||
trans.sa_session.flush()
|
||||
sa_session.flush()
|
||||
|
||||
def extract_capsule_files( trans, **kwd ):
|
||||
def create_repository_and_import_archive( app, host, user, repository_archive_dict, import_results_tups ):
|
||||
"""
|
||||
Create a new repository in the tool shed and populate it with the contents of a gzip compressed
|
||||
tar archive that was exported as part or all of the contents of a capsule.
|
||||
"""
|
||||
results_message = ''
|
||||
name = repository_archive_dict.get( 'name', None )
|
||||
username = repository_archive_dict.get( 'owner', None )
|
||||
if name is None or username is None:
|
||||
ok = False
|
||||
results_message += 'Import failed: required repository name <b>%s</b> or owner <b>%s</b> is missing.' % \
|
||||
( str( name ), str( username ))
|
||||
import_results_tups.append( ( ok, ( str( name ), str( username ) ), results_message ) )
|
||||
else:
|
||||
status = repository_archive_dict.get( 'status', None )
|
||||
if status is None:
|
||||
# The repository does not yet exist in this Tool Shed and the current user is authorized to import
|
||||
# the current archive file.
|
||||
type = repository_archive_dict.get( 'type', 'unrestricted' )
|
||||
description = repository_archive_dict.get( 'description', '' )
|
||||
long_description = repository_archive_dict.get( 'long_description', '' )
|
||||
# The owner entry in the repository_archive_dict is the public username of the user associated with
|
||||
# the exported repository archive.
|
||||
user = suc.get_user_by_username( app, username )
|
||||
if user is None:
|
||||
ok = False
|
||||
results_message += 'Import failed: repository owner <b>%s</b> does not have an account in this Tool Shed.' % str( username )
|
||||
import_results_tups.append( ( ok, ( str( name ), str( username ) ), results_message ) )
|
||||
else:
|
||||
user_id = user.id
|
||||
# The categories entry in the repository_archive_dict is a list of category names. If a name does not
|
||||
# exist in the current Tool Shed, the category will not be created, so it will not be associated with
|
||||
# the repository.
|
||||
category_ids = []
|
||||
category_names = repository_archive_dict.get( 'category_names', [] )
|
||||
for category_name in category_names:
|
||||
category = suc.get_category_by_name( app, category_name )
|
||||
if category is None:
|
||||
results_message += 'This Tool Shed does not have the category <b>%s</b> so it ' % str( category_name )
|
||||
results_message += 'will not be associated with this repository.'
|
||||
else:
|
||||
category_ids.append( app.security.encode_id( category.id ) )
|
||||
# Create the repository record in the database.
|
||||
repository, create_message = repository_maintenance_util.create_repository( app,
|
||||
name,
|
||||
type,
|
||||
description,
|
||||
long_description,
|
||||
user_id=user_id,
|
||||
category_ids=category_ids )
|
||||
if create_message:
|
||||
results_message += create_message
|
||||
# Populate the new repository with the contents of exported repository archive.
|
||||
results_dict = import_repository_archive( app, host, user, repository, repository_archive_dict )
|
||||
ok = results_dict.get( 'ok', False )
|
||||
error_message = results_dict.get( 'error_message', '' )
|
||||
if error_message:
|
||||
results_message += error_message
|
||||
import_results_tups.append( ( ok, ( str( name ), str( username ) ), results_message ) )
|
||||
else:
|
||||
# The repository either already exists in this Tool Shed or the current user is not authorized to create it.
|
||||
ok = True
|
||||
results_message += 'Import not necessary: repository status for this Tool Shed is: %s.' % str( status )
|
||||
import_results_tups.append( ( ok, ( str( name ), str( username ) ), results_message ) )
|
||||
return import_results_tups
|
||||
|
||||
def extract_capsule_files( **kwd ):
|
||||
"""Extract the uploaded capsule archive into a temporary location for inspection, validation and potential import."""
|
||||
return_dict = {}
|
||||
tar_archive = kwd.get( 'tar_archive', None )
|
||||
@@ -168,7 +235,7 @@ def get_repository_info_from_manifest( manifest_file_path ):
|
||||
repository_info_dicts.append( repository_info_dict )
|
||||
return repository_info_dicts, error_message
|
||||
|
||||
def get_repository_status_from_tool_shed( trans, repository_info_dicts ):
|
||||
def get_repository_status_from_tool_shed( app, user, user_is_admin, repository_info_dicts ):
|
||||
"""
|
||||
For each exported repository archive contained in the capsule, inspect the Tool Shed to see if that repository already
|
||||
exists or if the current user is authorized to create the repository, and set a status appropriately. If repository
|
||||
@@ -179,7 +246,7 @@ def get_repository_status_from_tool_shed( trans, repository_info_dicts ):
|
||||
"""
|
||||
repository_status_info_dicts = []
|
||||
for repository_info_dict in repository_info_dicts:
|
||||
repository = suc.get_repository_by_name_and_owner( trans.app, repository_info_dict[ 'name' ], repository_info_dict[ 'owner' ] )
|
||||
repository = suc.get_repository_by_name_and_owner( app, repository_info_dict[ 'name' ], repository_info_dict[ 'owner' ] )
|
||||
if repository:
|
||||
if repository.deleted:
|
||||
repository_info_dict[ 'status' ] = 'Exists, deleted'
|
||||
@@ -189,16 +256,16 @@ def get_repository_status_from_tool_shed( trans, repository_info_dicts ):
|
||||
repository_info_dict[ 'status' ] = 'Exists'
|
||||
else:
|
||||
# No repository with the specified name and owner currently exists, so make sure the current user can create one.
|
||||
if trans.user_is_admin():
|
||||
if user_is_admin:
|
||||
repository_info_dict[ 'status' ] = None
|
||||
elif trans.app.security_agent.user_can_import_repository_archive( trans.user, repository_info_dict[ 'owner' ] ):
|
||||
elif app.security_agent.user_can_import_repository_archive( user, repository_info_dict[ 'owner' ] ):
|
||||
repository_info_dict[ 'status' ] = None
|
||||
else:
|
||||
repository_info_dict[ 'status' ] = 'Not authorized to import'
|
||||
repository_status_info_dicts.append( repository_info_dict )
|
||||
return repository_status_info_dicts
|
||||
|
||||
def import_repository_archive( trans, repository, repository_archive_dict ):
|
||||
def import_repository_archive( app, host, user, repository, repository_archive_dict ):
|
||||
"""Import a repository archive contained within a repository capsule."""
|
||||
archive_file_name = repository_archive_dict.get( 'archive_file_name', None )
|
||||
capsule_file_name = repository_archive_dict[ 'capsule_file_name' ]
|
||||
@@ -207,8 +274,8 @@ def import_repository_archive( trans, repository, repository_archive_dict ):
|
||||
results_dict = dict( ok=True, error_message='' )
|
||||
archive_file_path = os.path.join( file_path, archive_file_name )
|
||||
archive = tarfile.open( archive_file_path, 'r:*' )
|
||||
repo_dir = repository.repo_path( trans.app )
|
||||
repo = hg_util.get_repo_for_repository( trans.app, repository=None, repo_path=repo_dir, create=False )
|
||||
repo_dir = repository.repo_path( app )
|
||||
repo = hg_util.get_repo_for_repository( app, repository=None, repo_path=repo_dir, create=False )
|
||||
undesirable_dirs_removed = 0
|
||||
undesirable_files_removed = 0
|
||||
ok, error_message = commit_util.check_archive( repository, archive )
|
||||
@@ -237,9 +304,8 @@ def import_repository_archive( trans, repository, repository_archive_dict ):
|
||||
uploaded_file_name = os.path.join( full_path, filename )
|
||||
if os.path.split( uploaded_file_name )[ -1 ] == rt_util.REPOSITORY_DEPENDENCY_DEFINITION_FILENAME:
|
||||
# Inspect the contents of the file to see if changeset_revision values are missing and if so, set them appropriately.
|
||||
altered, root_elem, error_message = commit_util.handle_repository_dependencies_definition( trans,
|
||||
uploaded_file_name,
|
||||
unpopulate=False )
|
||||
altered, root_elem, error_message = \
|
||||
commit_util.handle_repository_dependencies_definition( app, uploaded_file_name, unpopulate=False )
|
||||
if error_message:
|
||||
results_dict[ 'ok' ] = False
|
||||
results_dict[ 'error_message' ] += error_message
|
||||
@@ -248,7 +314,7 @@ def import_repository_archive( trans, repository, repository_archive_dict ):
|
||||
shutil.move( tmp_filename, uploaded_file_name )
|
||||
elif os.path.split( uploaded_file_name )[ -1 ] == rt_util.TOOL_DEPENDENCY_DEFINITION_FILENAME:
|
||||
# Inspect the contents of the file to see if changeset_revision values are missing and if so, set them appropriately.
|
||||
altered, root_elem, error_message = commit_util.handle_tool_dependencies_definition( trans, uploaded_file_name )
|
||||
altered, root_elem, error_message = commit_util.handle_tool_dependencies_definition( app, uploaded_file_name )
|
||||
if error_message:
|
||||
results_dict[ 'ok' ] = False
|
||||
results_dict[ 'error_message' ] += error_message
|
||||
@@ -261,7 +327,9 @@ def import_repository_archive( trans, repository, repository_archive_dict ):
|
||||
# Since the repository is new, the following must be False.
|
||||
remove_repo_files_not_in_tar = False
|
||||
ok, error_message, files_to_remove, content_alert_str, undesirable_dirs_removed, undesirable_files_removed = \
|
||||
commit_util.handle_directory_changes( trans,
|
||||
commit_util.handle_directory_changes( app,
|
||||
host,
|
||||
user.username,
|
||||
repository,
|
||||
full_path,
|
||||
filenames_in_archive,
|
||||
@@ -274,9 +342,14 @@ def import_repository_archive( trans, repository, repository_archive_dict ):
|
||||
results_dict[ 'ok' ] = False
|
||||
results_dict[ 'error_message' ] += error_message
|
||||
try:
|
||||
metadata_util.set_repository_metadata_due_to_new_tip( trans,
|
||||
repository,
|
||||
content_alert_str=content_alert_str )
|
||||
status, error_message = metadata_util.set_repository_metadata_due_to_new_tip( app,
|
||||
host,
|
||||
user,
|
||||
repository,
|
||||
content_alert_str=content_alert_str )
|
||||
if error_message:
|
||||
results_dict[ 'ok' ] = False
|
||||
results_dict[ 'error_message' ] += error_message
|
||||
except Exception, e:
|
||||
log.debug( "Error setting metadata on repository %s created from imported archive %s: %s" % \
|
||||
( str( repository.name ), str( archive_file_name ), str( e ) ) )
|
||||
@@ -286,7 +359,7 @@ def import_repository_archive( trans, repository, repository_archive_dict ):
|
||||
results_dict[ 'error_message' ] += error_message
|
||||
return results_dict
|
||||
|
||||
def upload_capsule( trans, **kwd ):
|
||||
def upload_capsule( **kwd ):
|
||||
"""Upload and prepare an exported repository capsule for validation."""
|
||||
file_data = kwd.get( 'file_data', '' )
|
||||
url = kwd.get( 'url', '' )
|
||||
@@ -349,7 +422,7 @@ def upload_capsule( trans, **kwd ):
|
||||
return return_dict
|
||||
return return_dict
|
||||
|
||||
def validate_capsule( trans, **kwd ):
|
||||
def validate_capsule( **kwd ):
|
||||
"""Inspect the uploaded capsule's manifest and its contained files to ensure it is a valid repository capsule."""
|
||||
capsule_dict = {}
|
||||
capsule_dict.update( kwd )
|
||||
|
||||
+182
-149
@@ -40,13 +40,14 @@ NOT_TOOL_CONFIGS = [ suc.DATATYPES_CONFIG_FILENAME,
|
||||
rt_util.TOOL_DEPENDENCY_DEFINITION_FILENAME,
|
||||
REPOSITORY_DATA_MANAGER_CONFIG_FILENAME ]
|
||||
|
||||
def add_tool_versions( trans, id, repository_metadata, changeset_revisions ):
|
||||
def add_tool_versions( app, id, repository_metadata, changeset_revisions ):
|
||||
# Build a dictionary of { 'tool id' : 'parent tool id' } pairs for each tool in repository_metadata.
|
||||
sa_session = app.model.context.current
|
||||
metadata = repository_metadata.metadata
|
||||
tool_versions_dict = {}
|
||||
for tool_dict in metadata.get( 'tools', [] ):
|
||||
# We have at least 2 changeset revisions to compare tool guids and tool ids.
|
||||
parent_id = get_parent_id( trans.app,
|
||||
parent_id = get_parent_id( app,
|
||||
id,
|
||||
tool_dict[ 'id' ],
|
||||
tool_dict[ 'version' ],
|
||||
@@ -55,8 +56,8 @@ def add_tool_versions( trans, id, repository_metadata, changeset_revisions ):
|
||||
tool_versions_dict[ tool_dict[ 'guid' ] ] = parent_id
|
||||
if tool_versions_dict:
|
||||
repository_metadata.tool_versions = tool_versions_dict
|
||||
trans.sa_session.add( repository_metadata )
|
||||
trans.sa_session.flush()
|
||||
sa_session.add( repository_metadata )
|
||||
sa_session.flush()
|
||||
|
||||
def clean_repository_metadata( trans, id, changeset_revisions ):
|
||||
# Delete all repository_metadata records associated with the repository that have a changeset_revision that is not in changeset_revisions.
|
||||
@@ -72,10 +73,15 @@ def clean_repository_metadata( trans, id, changeset_revisions ):
|
||||
trans.sa_session.delete( repository_metadata )
|
||||
trans.sa_session.flush()
|
||||
|
||||
def compare_changeset_revisions( trans, ancestor_changeset_revision, ancestor_metadata_dict, current_changeset_revision, current_metadata_dict ):
|
||||
"""Compare the contents of two changeset revisions to determine if a new repository metadata revision should be created."""
|
||||
# The metadata associated with ancestor_changeset_revision is ancestor_metadata_dict. This changeset_revision is an ancestor of
|
||||
# current_changeset_revision which is associated with current_metadata_dict. A new repository_metadata record will be created only
|
||||
def compare_changeset_revisions( app, ancestor_changeset_revision, ancestor_metadata_dict,
|
||||
current_changeset_revision, current_metadata_dict ):
|
||||
"""
|
||||
Compare the contents of two changeset revisions to determine if a new repository
|
||||
metadata revision should be created.
|
||||
"""
|
||||
# The metadata associated with ancestor_changeset_revision is ancestor_metadata_dict.
|
||||
# This changeset_revision is an ancestor of current_changeset_revision which is associated
|
||||
# with current_metadata_dict. A new repository_metadata record will be created only
|
||||
# when this method returns the constant value NOT_EQUAL_AND_NOT_SUBSET.
|
||||
ancestor_datatypes = ancestor_metadata_dict.get( 'datatypes', [] )
|
||||
ancestor_tools = ancestor_metadata_dict.get( 'tools', [] )
|
||||
@@ -107,14 +113,16 @@ def compare_changeset_revisions( trans, ancestor_changeset_revision, ancestor_me
|
||||
no_data_manager = not ancestor_data_manager and not current_data_manager
|
||||
if no_datatypes and no_readme_files and no_repository_dependencies and no_tool_dependencies and no_tools and no_workflows and no_data_manager:
|
||||
return NO_METADATA
|
||||
# Uncomment the following if we decide that README files should affect how installable repository revisions are defined. See the NOTE in the
|
||||
# compare_readme_files() method.
|
||||
# Uncomment the following if we decide that README files should affect how installable
|
||||
# repository revisions are defined. See the NOTE in the compare_readme_files() method.
|
||||
# readme_file_comparision = compare_readme_files( ancestor_readme_files, current_readme_files )
|
||||
repository_dependency_comparison = compare_repository_dependencies( trans, ancestor_repository_dependencies, current_repository_dependencies )
|
||||
tool_dependency_comparison = compare_tool_dependencies( trans, ancestor_tool_dependencies, current_tool_dependencies )
|
||||
workflow_comparison = compare_workflows( trans, ancestor_workflows, current_workflows )
|
||||
datatype_comparison = compare_datatypes( trans, ancestor_datatypes, current_datatypes )
|
||||
data_manager_comparison = compare_data_manager( trans, ancestor_data_manager, current_data_manager )
|
||||
repository_dependency_comparison = compare_repository_dependencies( app,
|
||||
ancestor_repository_dependencies,
|
||||
current_repository_dependencies )
|
||||
tool_dependency_comparison = compare_tool_dependencies( ancestor_tool_dependencies, current_tool_dependencies )
|
||||
workflow_comparison = compare_workflows( ancestor_workflows, current_workflows )
|
||||
datatype_comparison = compare_datatypes( ancestor_datatypes, current_datatypes )
|
||||
data_manager_comparison = compare_data_manager( ancestor_data_manager, current_data_manager )
|
||||
# Handle case where all metadata is the same.
|
||||
if ancestor_guids == current_guids and \
|
||||
repository_dependency_comparison == EQUAL and \
|
||||
@@ -130,7 +138,11 @@ def compare_changeset_revisions( trans, ancestor_changeset_revision, ancestor_me
|
||||
workflow_dependency_is_subset = workflow_comparison in SUBSET_VALUES
|
||||
datatype_is_subset = datatype_comparison in SUBSET_VALUES
|
||||
datamanager_is_subset = data_manager_comparison in SUBSET_VALUES
|
||||
if repository_dependency_is_subset and tool_dependency_is_subset and workflow_dependency_is_subset and datatype_is_subset and datamanager_is_subset:
|
||||
if repository_dependency_is_subset and \
|
||||
tool_dependency_is_subset and \
|
||||
workflow_dependency_is_subset and \
|
||||
datatype_is_subset and \
|
||||
datamanager_is_subset:
|
||||
is_subset = True
|
||||
for guid in ancestor_guids:
|
||||
if guid not in current_guids:
|
||||
@@ -140,11 +152,16 @@ def compare_changeset_revisions( trans, ancestor_changeset_revision, ancestor_me
|
||||
return SUBSET
|
||||
return NOT_EQUAL_AND_NOT_SUBSET
|
||||
|
||||
def compare_data_manager( trans, ancestor_metadata, current_metadata ):
|
||||
def compare_data_manager( ancestor_metadata, current_metadata ):
|
||||
"""Determine if ancestor_metadata is the same as or a subset of current_metadata for data_managers."""
|
||||
def __data_manager_dict_to_tuple_list( metadata_dict ):
|
||||
# we do not check tool_guid or tool conf file name
|
||||
return set( sorted( [ ( name, tuple( sorted( value.get( 'data_tables', [] ) ) ), value.get( 'guid' ), value.get( 'version' ), value.get( 'name' ), value.get( 'id' ) ) for name, value in metadata_dict.iteritems() ] ) )
|
||||
return set( sorted( [ ( name,
|
||||
tuple( sorted( value.get( 'data_tables', [] ) ) ),
|
||||
value.get( 'guid' ),
|
||||
value.get( 'version' ),
|
||||
value.get( 'name' ),
|
||||
value.get( 'id' ) ) for name, value in metadata_dict.iteritems() ] ) )
|
||||
# only compare valid entries, any invalid entries are ignored
|
||||
ancestor_metadata = __data_manager_dict_to_tuple_list( ancestor_metadata.get( 'data_managers', {} ) )
|
||||
current_metadata = __data_manager_dict_to_tuple_list( current_metadata.get( 'data_managers', {} ) )
|
||||
@@ -155,7 +172,7 @@ def compare_data_manager( trans, ancestor_metadata, current_metadata ):
|
||||
return SUBSET
|
||||
return NOT_EQUAL_AND_NOT_SUBSET
|
||||
|
||||
def compare_datatypes( trans, ancestor_datatypes, current_datatypes ):
|
||||
def compare_datatypes( ancestor_datatypes, current_datatypes ):
|
||||
"""Determine if ancestor_datatypes is the same as or a subset of current_datatypes."""
|
||||
# Each datatype dict looks something like: {"dtype": "galaxy.datatypes.images:Image", "extension": "pdf", "mimetype": "application/pdf"}
|
||||
if len( ancestor_datatypes ) <= len( current_datatypes ):
|
||||
@@ -199,8 +216,10 @@ def compare_readme_files( ancestor_readme_files, current_readme_files ):
|
||||
return SUBSET
|
||||
return NOT_EQUAL_AND_NOT_SUBSET
|
||||
|
||||
def compare_repository_dependencies( trans, ancestor_repository_dependencies, current_repository_dependencies ):
|
||||
"""Determine if ancestor_repository_dependencies is the same as or a subset of current_repository_dependencies."""
|
||||
def compare_repository_dependencies( app, ancestor_repository_dependencies, current_repository_dependencies ):
|
||||
"""
|
||||
Determine if ancestor_repository_dependencies is the same as or a subset of current_repository_dependencies.
|
||||
"""
|
||||
# The list of repository_dependencies looks something like:
|
||||
# [["http://localhost:9009", "emboss_datatypes", "test", "ab03a2a5f407", "False", "False"]].
|
||||
# Create a string from each tuple in the list for easier comparison.
|
||||
@@ -223,9 +242,12 @@ def compare_repository_dependencies( trans, ancestor_repository_dependencies, cu
|
||||
found_in_current = True
|
||||
break
|
||||
if not found_in_current:
|
||||
# In some cases, the only difference between a dependency definition in the lists is the changeset_revision value. We'll
|
||||
# check to see if this is the case, and if the defined dependency is a repository that has metadata set only on its tip.
|
||||
if not different_revision_defines_tip_only_repository_dependency( trans, ancestor_tup, current_repository_dependencies ):
|
||||
# In some cases, the only difference between a dependency definition in the lists
|
||||
# is the changeset_revision value. We'll check to see if this is the case, and if
|
||||
# the defined dependency is a repository that has metadata set only on its tip.
|
||||
if not different_revision_defines_tip_only_repository_dependency( app,
|
||||
ancestor_tup,
|
||||
current_repository_dependencies ):
|
||||
return NOT_EQUAL_AND_NOT_SUBSET
|
||||
return SUBSET
|
||||
if len( ancestor_repository_dependencies ) == len( current_repository_dependencies ):
|
||||
@@ -234,16 +256,17 @@ def compare_repository_dependencies( trans, ancestor_repository_dependencies, cu
|
||||
return SUBSET
|
||||
return NOT_EQUAL_AND_NOT_SUBSET
|
||||
|
||||
def compare_tool_dependencies( trans, ancestor_tool_dependencies, current_tool_dependencies ):
|
||||
def compare_tool_dependencies( ancestor_tool_dependencies, current_tool_dependencies ):
|
||||
"""Determine if ancestor_tool_dependencies is the same as or a subset of current_tool_dependencies."""
|
||||
# The tool_dependencies dictionary looks something like:
|
||||
# {'bwa/0.5.9': {'readme': 'some string', 'version': '0.5.9', 'type': 'package', 'name': 'bwa'}}
|
||||
if len( ancestor_tool_dependencies ) <= len( current_tool_dependencies ):
|
||||
for ancestor_td_key, ancestor_requirements_dict in ancestor_tool_dependencies.items():
|
||||
if ancestor_td_key in current_tool_dependencies:
|
||||
# The only values that could have changed between the 2 dictionaries are the "readme" or "type" values. Changing the readme value
|
||||
# makes no difference. Changing the type will change the installation process, but for now we'll assume it was a typo, so new metadata
|
||||
# shouldn't be generated.
|
||||
# The only values that could have changed between the 2 dictionaries are the
|
||||
# "readme" or "type" values. Changing the readme value makes no difference.
|
||||
# Changing the type will change the installation process, but for now we'll
|
||||
# assume it was a typo, so new metadata shouldn't be generated.
|
||||
continue
|
||||
else:
|
||||
return NOT_EQUAL_AND_NOT_SUBSET
|
||||
@@ -254,8 +277,11 @@ def compare_tool_dependencies( trans, ancestor_tool_dependencies, current_tool_d
|
||||
return SUBSET
|
||||
return NOT_EQUAL_AND_NOT_SUBSET
|
||||
|
||||
def compare_workflows( trans, ancestor_workflows, current_workflows ):
|
||||
"""Determine if ancestor_workflows is the same as current_workflows or if ancestor_workflows is a subset of current_workflows."""
|
||||
def compare_workflows( ancestor_workflows, current_workflows ):
|
||||
"""
|
||||
Determine if ancestor_workflows is the same as current_workflows or if ancestor_workflows
|
||||
is a subset of current_workflows.
|
||||
"""
|
||||
if len( ancestor_workflows ) <= len( current_workflows ):
|
||||
for ancestor_workflow_tup in ancestor_workflows:
|
||||
# ancestor_workflows is a list of tuples where each contained tuple is
|
||||
@@ -267,8 +293,10 @@ def compare_workflows( trans, ancestor_workflows, current_workflows ):
|
||||
found_in_current = False
|
||||
for current_workflow_tup in current_workflows:
|
||||
current_workflow_dict = current_workflow_tup[1]
|
||||
# Assume that if the name and number of steps are euqal, then the workflows are the same. Of course, this may not be true...
|
||||
if current_workflow_dict[ 'name' ] == ancestor_workflow_name and len( current_workflow_dict[ 'steps' ] ) == num_ancestor_workflow_steps:
|
||||
# Assume that if the name and number of steps are euqal, then the workflows
|
||||
# are the same. Of course, this may not be true...
|
||||
if current_workflow_dict[ 'name' ] == ancestor_workflow_name and \
|
||||
len( current_workflow_dict[ 'steps' ] ) == num_ancestor_workflow_steps:
|
||||
found_in_current = True
|
||||
break
|
||||
if not found_in_current:
|
||||
@@ -279,8 +307,9 @@ def compare_workflows( trans, ancestor_workflows, current_workflows ):
|
||||
return SUBSET
|
||||
return NOT_EQUAL_AND_NOT_SUBSET
|
||||
|
||||
def create_or_update_repository_metadata( trans, id, repository, changeset_revision, metadata_dict ):
|
||||
def create_or_update_repository_metadata( app, id, repository, changeset_revision, metadata_dict ):
|
||||
"""Create or update a repository_metadatqa record in the tool shed."""
|
||||
sa_session = app.model.context.current
|
||||
has_repository_dependencies = False
|
||||
has_repository_dependencies_only_if_compiling_contained_td = False
|
||||
includes_datatypes = False
|
||||
@@ -309,7 +338,7 @@ def create_or_update_repository_metadata( trans, id, repository, changeset_revis
|
||||
downloadable = True
|
||||
else:
|
||||
downloadable = False
|
||||
repository_metadata = suc.get_repository_metadata_by_changeset_revision( trans.app, id, changeset_revision )
|
||||
repository_metadata = suc.get_repository_metadata_by_changeset_revision( app, id, changeset_revision )
|
||||
if repository_metadata:
|
||||
# A repository metadata record already exists with the received changeset_revision, so we don't need to
|
||||
# check the skip_tool_test table.
|
||||
@@ -325,15 +354,15 @@ def create_or_update_repository_metadata( trans, id, repository, changeset_revis
|
||||
# No repository_metadata record exists for the received changeset_revision, so we may need to update the
|
||||
# skip_tool_test table.
|
||||
check_skip_tool_test = True
|
||||
repository_metadata = trans.model.RepositoryMetadata( repository_id=repository.id,
|
||||
changeset_revision=changeset_revision,
|
||||
metadata=metadata_dict,
|
||||
downloadable=downloadable,
|
||||
has_repository_dependencies=has_repository_dependencies,
|
||||
includes_datatypes=includes_datatypes,
|
||||
includes_tools=includes_tools,
|
||||
includes_tool_dependencies=includes_tool_dependencies,
|
||||
includes_workflows=includes_workflows )
|
||||
repository_metadata = app.model.RepositoryMetadata( repository_id=repository.id,
|
||||
changeset_revision=changeset_revision,
|
||||
metadata=metadata_dict,
|
||||
downloadable=downloadable,
|
||||
has_repository_dependencies=has_repository_dependencies,
|
||||
includes_datatypes=includes_datatypes,
|
||||
includes_tools=includes_tools,
|
||||
includes_tool_dependencies=includes_tool_dependencies,
|
||||
includes_workflows=includes_workflows )
|
||||
# Always set the default values for the following columns. When resetting all metadata on a repository
|
||||
# this will reset the values.
|
||||
repository_metadata.tools_functionally_correct = False
|
||||
@@ -342,20 +371,20 @@ def create_or_update_repository_metadata( trans, id, repository, changeset_revis
|
||||
repository_metadata.do_not_test = False
|
||||
repository_metadata.time_last_tested = None
|
||||
repository_metadata.tool_test_results = None
|
||||
trans.sa_session.add( repository_metadata )
|
||||
trans.sa_session.flush()
|
||||
sa_session.add( repository_metadata )
|
||||
sa_session.flush()
|
||||
if check_skip_tool_test:
|
||||
# Since we created a new repository_metadata record, we may need to update the skip_tool_test table to point to it. Inspect each
|
||||
# changeset revision in the received repository's changelog (up to the received changeset revision) to see if it is contained in the
|
||||
# skip_tool_test table. If it is, but is not associated with a repository_metadata record, reset that skip_tool_test record to the
|
||||
# newly created repository_metadata record.
|
||||
repo = hg_util.get_repo_for_repository( trans.app, repository=repository, repo_path=None, create=False )
|
||||
repo = hg_util.get_repo_for_repository( app, repository=repository, repo_path=None, create=False )
|
||||
for changeset in repo.changelog:
|
||||
changeset_hash = str( repo.changectx( changeset ) )
|
||||
skip_tool_test = suc.get_skip_tool_test_by_changeset_revision( trans.app, changeset_hash )
|
||||
skip_tool_test = suc.get_skip_tool_test_by_changeset_revision( app, changeset_hash )
|
||||
if skip_tool_test:
|
||||
# We found a skip_tool_test record associated with the changeset_revision, so see if it has a valid repository_revision.
|
||||
repository_revision = get_repository_metadata_by_id( trans.app, trans.security.encode_id( repository_metadata.id ) )
|
||||
repository_revision = get_repository_metadata_by_id( app, app.security.encode_id( repository_metadata.id ) )
|
||||
if repository_revision:
|
||||
# The skip_tool_test record is associated with a valid repository_metadata record, so proceed.
|
||||
continue
|
||||
@@ -364,14 +393,14 @@ def create_or_update_repository_metadata( trans, id, repository, changeset_revis
|
||||
# require updating, so we won't break here, we'll continue to inspect the rest of the changelog up to the received
|
||||
# changeset_revision.
|
||||
skip_tool_test.repository_metadata_id = repository_metadata.id
|
||||
trans.sa_session.add( skip_tool_test )
|
||||
trans.sa_session.flush()
|
||||
sa_session.add( skip_tool_test )
|
||||
sa_session.flush()
|
||||
if changeset_hash == changeset_revision:
|
||||
# Proceed no further than the received changeset_revision.
|
||||
break
|
||||
return repository_metadata
|
||||
|
||||
def different_revision_defines_tip_only_repository_dependency( trans, rd_tup, repository_dependencies ):
|
||||
def different_revision_defines_tip_only_repository_dependency( app, rd_tup, repository_dependencies ):
|
||||
"""
|
||||
Determine if the only difference between rd_tup and a dependency definition in the list of
|
||||
repository_dependencies is the changeset_revision value.
|
||||
@@ -386,8 +415,8 @@ def different_revision_defines_tip_only_repository_dependency( trans, rd_tup, re
|
||||
cleaned_tool_shed = common_util.remove_protocol_from_tool_shed_url( tool_shed )
|
||||
if cleaned_rd_tool_shed == cleaned_tool_shed and rd_name == name and rd_owner == owner:
|
||||
# Determine if the repository represented by the dependency tuple is an instance of the repository type TipOnly.
|
||||
required_repository = suc.get_repository_by_name_and_owner( trans.app, name, owner )
|
||||
repository_type_class = trans.app.repository_types_registry.get_class_by_label( required_repository.type )
|
||||
required_repository = suc.get_repository_by_name_and_owner( app, name, owner )
|
||||
repository_type_class = app.repository_types_registry.get_class_by_label( required_repository.type )
|
||||
return isinstance( repository_type_class, TipOnly )
|
||||
return False
|
||||
|
||||
@@ -1045,20 +1074,17 @@ def generate_workflow_metadata( relative_path, exported_workflow_dict, metadata_
|
||||
metadata_dict[ 'workflows' ] = [ ( relative_path, exported_workflow_dict ) ]
|
||||
return metadata_dict
|
||||
|
||||
def get_latest_repository_metadata( trans, decoded_repository_id, downloadable=False ):
|
||||
def get_latest_repository_metadata( app, decoded_repository_id, downloadable=False ):
|
||||
"""Get last metadata defined for a specified repository from the database."""
|
||||
repository = trans.sa_session.query( trans.model.Repository ).get( decoded_repository_id )
|
||||
repo = hg_util.get_repo_for_repository( trans.app, repository=repository, repo_path=None, create=False )
|
||||
sa_session = app.model.context.current
|
||||
repository = sa_session.query( app.model.Repository ).get( decoded_repository_id )
|
||||
repo = hg_util.get_repo_for_repository( app, repository=repository, repo_path=None, create=False )
|
||||
if downloadable:
|
||||
changeset_revision = suc.get_latest_downloadable_changeset_revision( trans.app,
|
||||
repository,
|
||||
repo )
|
||||
changeset_revision = suc.get_latest_downloadable_changeset_revision( app, repository, repo )
|
||||
else:
|
||||
changeset_revision = suc.get_latest_changeset_revision( trans.app,
|
||||
repository,
|
||||
repo )
|
||||
return suc.get_repository_metadata_by_changeset_revision( trans.app,
|
||||
trans.security.encode_id( repository.id ),
|
||||
changeset_revision = suc.get_latest_changeset_revision( app, repository, repo )
|
||||
return suc.get_repository_metadata_by_changeset_revision( app,
|
||||
app.security.encode_id( repository.id ),
|
||||
changeset_revision )
|
||||
|
||||
def get_parent_id( app, id, old_id, version, guid, changeset_revisions ):
|
||||
@@ -1377,10 +1403,11 @@ def is_malicious( app, id, changeset_revision, **kwd ):
|
||||
return repository_metadata.malicious
|
||||
return False
|
||||
|
||||
def new_datatypes_metadata_required( trans, repository_metadata, metadata_dict ):
|
||||
def new_datatypes_metadata_required( repository_metadata, metadata_dict ):
|
||||
"""
|
||||
Compare the last saved metadata for each datatype in the repository with the new metadata in metadata_dict to determine if a new
|
||||
repository_metadata table record is required or if the last saved metadata record can be updated for datatypes instead.
|
||||
Compare the last saved metadata for each datatype in the repository with the new metadata
|
||||
in metadata_dict to determine if a new repository_metadata table record is required or if
|
||||
the last saved metadata record can be updated for datatypes instead.
|
||||
"""
|
||||
# Datatypes are stored in metadata as a list of dictionaries that looks like:
|
||||
# [{'dtype': 'galaxy.datatypes.data:Text', 'subclass': 'True', 'extension': 'acedb'}]
|
||||
@@ -1392,7 +1419,7 @@ def new_datatypes_metadata_required( trans, repository_metadata, metadata_dict )
|
||||
if 'datatypes' in metadata:
|
||||
ancestor_datatypes = metadata[ 'datatypes' ]
|
||||
# The saved metadata must be a subset of the new metadata.
|
||||
datatype_comparison = compare_datatypes( trans, ancestor_datatypes, current_datatypes )
|
||||
datatype_comparison = compare_datatypes( ancestor_datatypes, current_datatypes )
|
||||
if datatype_comparison == NOT_EQUAL_AND_NOT_SUBSET:
|
||||
return True
|
||||
else:
|
||||
@@ -1409,23 +1436,28 @@ def new_datatypes_metadata_required( trans, repository_metadata, metadata_dict )
|
||||
# The received metadata_dict includes no metadata for datatypes, so a new repository_metadata table record is not needed.
|
||||
return False
|
||||
|
||||
def new_metadata_required_for_utilities( trans, repository, new_tip_metadata_dict ):
|
||||
def new_metadata_required_for_utilities( app, repository, new_tip_metadata_dict ):
|
||||
"""
|
||||
Galaxy utilities currently consist of datatypes, repository_dependency definitions, tools, tool_dependency definitions and exported
|
||||
Galaxy workflows. This method compares the last stored repository_metadata record associated with the received repository against the
|
||||
contents of the received new_tip_metadata_dict and returns True or False for the union set of Galaxy utilities contained in both metadata
|
||||
dictionaries. The metadata contained in new_tip_metadata_dict may not be a subset of that contained in the last stored repository_metadata
|
||||
record associated with the received repository because one or more Galaxy utilities may have been deleted from the repository in the new tip.
|
||||
Galaxy utilities currently consist of datatypes, repository_dependency definitions,
|
||||
tools, tool_dependency definitions and exported Galaxy workflows. This method compares
|
||||
the last stored repository_metadata record associated with the received repository against
|
||||
the contents of the received new_tip_metadata_dict and returns True or False for the union
|
||||
set of Galaxy utilities contained in both metadata dictionaries. The metadata contained
|
||||
in new_tip_metadata_dict may not be a subset of that contained in the last stored
|
||||
repository_metadata record associated with the received repository because one or more
|
||||
Galaxy utilities may have been deleted from the repository in the new tip.
|
||||
"""
|
||||
repository_metadata = get_latest_repository_metadata( trans, repository.id, downloadable=False )
|
||||
datatypes_required = new_datatypes_metadata_required( trans, repository_metadata, new_tip_metadata_dict )
|
||||
# Uncomment the following if we decide that README files should affect how installable repository revisions are defined. See the NOTE in the
|
||||
# compare_readme_files() method.
|
||||
# readme_files_required = new_readme_files_metadata_required( trans, repository_metadata, new_tip_metadata_dict )
|
||||
repository_dependencies_required = new_repository_dependency_metadata_required( trans, repository_metadata, new_tip_metadata_dict )
|
||||
tools_required = new_tool_metadata_required( trans, repository_metadata, new_tip_metadata_dict )
|
||||
tool_dependencies_required = new_tool_dependency_metadata_required( trans, repository_metadata, new_tip_metadata_dict )
|
||||
workflows_required = new_workflow_metadata_required( trans, repository_metadata, new_tip_metadata_dict )
|
||||
repository_metadata = get_latest_repository_metadata( app, repository.id, downloadable=False )
|
||||
datatypes_required = new_datatypes_metadata_required( repository_metadata, new_tip_metadata_dict )
|
||||
# Uncomment the following if we decide that README files should affect how installable
|
||||
# repository revisions are defined. See the NOTE in the compare_readme_files() method.
|
||||
# readme_files_required = new_readme_files_metadata_required( repository_metadata, new_tip_metadata_dict )
|
||||
repository_dependencies_required = new_repository_dependency_metadata_required( app,
|
||||
repository_metadata,
|
||||
new_tip_metadata_dict )
|
||||
tools_required = new_tool_metadata_required( repository_metadata, new_tip_metadata_dict )
|
||||
tool_dependencies_required = new_tool_dependency_metadata_required( repository_metadata, new_tip_metadata_dict )
|
||||
workflows_required = new_workflow_metadata_required( repository_metadata, new_tip_metadata_dict )
|
||||
if datatypes_required or repository_dependencies_required or tools_required or tool_dependencies_required or workflows_required:
|
||||
return True
|
||||
return False
|
||||
@@ -1462,7 +1494,7 @@ def new_readme_files_metadata_required( trans, repository_metadata, metadata_dic
|
||||
# The received metadata_dict includes no metadata for readme_files, so a new repository_metadata table record is not needed.
|
||||
return False
|
||||
|
||||
def new_repository_dependency_metadata_required( trans, repository_metadata, metadata_dict ):
|
||||
def new_repository_dependency_metadata_required( app, repository_metadata, metadata_dict ):
|
||||
"""
|
||||
Compare the last saved metadata for each repository dependency in the repository with the new metadata in metadata_dict to determine if a new
|
||||
repository_metadata table record is required or if the last saved metadata record can be updated for repository_dependencies instead.
|
||||
@@ -1474,24 +1506,29 @@ def new_repository_dependency_metadata_required( trans, repository_metadata, met
|
||||
new_repository_dependencies_metadata = metadata_dict.get( 'repository_dependencies', None )
|
||||
if new_repository_dependencies_metadata:
|
||||
new_repository_dependencies = metadata_dict[ 'repository_dependencies' ][ 'repository_dependencies' ]
|
||||
# TODO: We used to include the following here to handle the case where repository dependency definitions were deleted. However,
|
||||
# this erroneously returned True in cases where is should not have done so. This usually occurred where multiple single files
|
||||
# were uploaded when a single tarball should have been. We need to implement support for handling deleted repository dependency
|
||||
# definitions so that we can guarantee reproducibility, but we need to do it in a way that is better than the following.
|
||||
# TODO: We used to include the following here to handle the case where repository
|
||||
# dependency definitions were deleted. However this erroneously returned True in
|
||||
# cases where is should not have done so. This usually occurred where multiple single
|
||||
# files were uploaded when a single tarball should have been. We need to implement
|
||||
# support for handling deleted repository dependency definitions so that we can guarantee
|
||||
# reproducibility, but we need to do it in a way that is better than the following.
|
||||
# for new_repository_dependency in new_repository_dependencies:
|
||||
# if new_repository_dependency not in saved_repository_dependencies:
|
||||
# return True
|
||||
# The saved metadata must be a subset of the new metadata.
|
||||
for saved_repository_dependency in saved_repository_dependencies:
|
||||
if saved_repository_dependency not in new_repository_dependencies:
|
||||
# In some cases, the only difference between a dependency definition in the lists is the changeset_revision value. We'll
|
||||
# check to see if this is the case, and if the defined dependency is a repository that has metadata set only on its tip.
|
||||
if not different_revision_defines_tip_only_repository_dependency( trans, saved_repository_dependency, new_repository_dependencies ):
|
||||
# In some cases, the only difference between a dependency definition in the lists
|
||||
# is the changeset_revision value. We'll check to see if this is the case, and if
|
||||
# the defined dependency is a repository that has metadata set only on its tip.
|
||||
if not different_revision_defines_tip_only_repository_dependency( app,
|
||||
saved_repository_dependency,
|
||||
new_repository_dependencies ):
|
||||
return True
|
||||
return False
|
||||
else:
|
||||
# The repository_dependencies.xml file must have been deleted, so create a new repository_metadata record so we always have
|
||||
# access to the deleted file.
|
||||
# The repository_dependencies.xml file must have been deleted, so create a new
|
||||
# repository_metadata record so we always have access to the deleted file.
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
@@ -1500,13 +1537,15 @@ def new_repository_dependency_metadata_required( trans, repository_metadata, met
|
||||
# There is no saved repository metadata, so we need to create a new repository_metadata record.
|
||||
return True
|
||||
else:
|
||||
# The received metadata_dict includes no metadata for repository dependencies, so a new repository_metadata record is not needed.
|
||||
# The received metadata_dict includes no metadata for repository dependencies, so
|
||||
# a new repository_metadata record is not needed.
|
||||
return False
|
||||
|
||||
def new_tool_dependency_metadata_required( trans, repository_metadata, metadata_dict ):
|
||||
def new_tool_dependency_metadata_required( repository_metadata, metadata_dict ):
|
||||
"""
|
||||
Compare the last saved metadata for each tool dependency in the repository with the new metadata in metadata_dict to determine if a new
|
||||
repository_metadata table record is required or if the last saved metadata record can be updated for tool_dependencies instead.
|
||||
Compare the last saved metadata for each tool dependency in the repository with the new
|
||||
metadata in metadata_dict to determine if a new repository_metadata table record is required
|
||||
or if the last saved metadata record can be updated for tool_dependencies instead.
|
||||
"""
|
||||
if repository_metadata:
|
||||
metadata = repository_metadata.metadata
|
||||
@@ -1545,10 +1584,11 @@ def new_tool_dependency_metadata_required( trans, repository_metadata, metadata_
|
||||
# The received metadata_dict includes no metadata for tool dependencies, so a new repository_metadata record is not needed.
|
||||
return False
|
||||
|
||||
def new_tool_metadata_required( trans, repository_metadata, metadata_dict ):
|
||||
def new_tool_metadata_required( repository_metadata, metadata_dict ):
|
||||
"""
|
||||
Compare the last saved metadata for each tool in the repository with the new metadata in metadata_dict to determine if a new repository_metadata
|
||||
table record is required, or if the last saved metadata record can be updated instead.
|
||||
Compare the last saved metadata for each tool in the repository with the new metadata in
|
||||
metadata_dict to determine if a new repository_metadata table record is required, or if
|
||||
the last saved metadata record can be updated instead.
|
||||
"""
|
||||
if 'tools' in metadata_dict:
|
||||
if repository_metadata:
|
||||
@@ -1587,10 +1627,11 @@ def new_tool_metadata_required( trans, repository_metadata, metadata_dict ):
|
||||
# The received metadata_dict includes no metadata for tools, so a new repository_metadata table record is not needed.
|
||||
return False
|
||||
|
||||
def new_workflow_metadata_required( trans, repository_metadata, metadata_dict ):
|
||||
def new_workflow_metadata_required( repository_metadata, metadata_dict ):
|
||||
"""
|
||||
Currently everything about an exported workflow except the name is hard-coded, so there's no real way to differentiate versions of
|
||||
exported workflows. If this changes at some future time, this method should be enhanced accordingly.
|
||||
Currently everything about an exported workflow except the name is hard-coded, so
|
||||
there's no real way to differentiate versions of exported workflows. If this changes
|
||||
at some future time, this method should be enhanced accordingly.
|
||||
"""
|
||||
if 'workflows' in metadata_dict:
|
||||
if repository_metadata:
|
||||
@@ -1819,7 +1860,7 @@ def reset_all_metadata_on_repository_in_tool_shed( trans, id ):
|
||||
# SUBSET - ancestor metadata is a subset of current metadata, so continue from current
|
||||
# NOT_EQUAL_AND_NOT_SUBSET - ancestor metadata is neither equal to nor a subset of current
|
||||
# metadata, so persist ancestor metadata.
|
||||
comparison = compare_changeset_revisions( trans,
|
||||
comparison = compare_changeset_revisions( trans.app,
|
||||
ancestor_changeset_revision,
|
||||
ancestor_metadata_dict,
|
||||
current_changeset_revision,
|
||||
@@ -1830,7 +1871,7 @@ def reset_all_metadata_on_repository_in_tool_shed( trans, id ):
|
||||
elif comparison == NOT_EQUAL_AND_NOT_SUBSET:
|
||||
metadata_changeset_revision = ancestor_changeset_revision
|
||||
metadata_dict = ancestor_metadata_dict
|
||||
repository_metadata = create_or_update_repository_metadata( trans,
|
||||
repository_metadata = create_or_update_repository_metadata( trans.app,
|
||||
id,
|
||||
repository,
|
||||
metadata_changeset_revision,
|
||||
@@ -1846,7 +1887,7 @@ def reset_all_metadata_on_repository_in_tool_shed( trans, id ):
|
||||
metadata_changeset_revision = current_changeset_revision
|
||||
metadata_dict = current_metadata_dict
|
||||
# We're at the end of the change log.
|
||||
repository_metadata = create_or_update_repository_metadata( trans,
|
||||
repository_metadata = create_or_update_repository_metadata( trans.app,
|
||||
id,
|
||||
repository,
|
||||
metadata_changeset_revision,
|
||||
@@ -1858,7 +1899,7 @@ def reset_all_metadata_on_repository_in_tool_shed( trans, id ):
|
||||
# We reach here only if current_metadata_dict is empty and ancestor_metadata_dict is not.
|
||||
if not ctx.children():
|
||||
# We're at the end of the change log.
|
||||
repository_metadata = create_or_update_repository_metadata( trans,
|
||||
repository_metadata = create_or_update_repository_metadata( trans.app,
|
||||
id,
|
||||
repository,
|
||||
metadata_changeset_revision,
|
||||
@@ -1901,7 +1942,7 @@ def reset_metadata_on_selected_repositories( trans, **kwd ):
|
||||
owner = str( repository.owner )
|
||||
invalid_file_tups, metadata_dict = reset_all_metadata_on_installed_repository( trans.app, repository_id )
|
||||
if invalid_file_tups:
|
||||
message = tool_util.generate_message_for_invalid_tools( trans, invalid_file_tups, repository, None, as_html=False )
|
||||
message = tool_util.generate_message_for_invalid_tools( trans.app, invalid_file_tups, repository, None, as_html=False )
|
||||
log.debug( message )
|
||||
unsuccessful_count += 1
|
||||
else:
|
||||
@@ -1947,20 +1988,21 @@ def set_add_to_tool_panel_attribute_for_tool( tool, guid, datatypes ):
|
||||
return False
|
||||
return True
|
||||
|
||||
def set_repository_metadata( trans, repository, content_alert_str='', **kwd ):
|
||||
def set_repository_metadata( app, host, user, repository, content_alert_str='', **kwd ):
|
||||
"""
|
||||
Set metadata using the repository's current disk files, returning specific error messages (if any) to alert the
|
||||
repository owner that the changeset has problems.
|
||||
Set metadata using the repository's current disk files, returning specific error
|
||||
messages (if any) to alert the repository owner that the changeset has problems.
|
||||
"""
|
||||
sa_session = app.model.context.current
|
||||
message = ''
|
||||
status = 'done'
|
||||
encoded_id = trans.security.encode_id( repository.id )
|
||||
repository_clone_url = common_util.generate_clone_url_for_repository_in_tool_shed( trans.user, repository )
|
||||
repo_dir = repository.repo_path( trans.app )
|
||||
repo = hg_util.get_repo_for_repository( trans.app, repository=None, repo_path=repo_dir, create=False )
|
||||
metadata_dict, invalid_file_tups = generate_metadata_for_changeset_revision( app=trans.app,
|
||||
encoded_id = app.security.encode_id( repository.id )
|
||||
repository_clone_url = common_util.generate_clone_url_for_repository_in_tool_shed( user, repository )
|
||||
repo_dir = repository.repo_path( app )
|
||||
repo = hg_util.get_repo_for_repository( app, repository=None, repo_path=repo_dir, create=False )
|
||||
metadata_dict, invalid_file_tups = generate_metadata_for_changeset_revision( app=app,
|
||||
repository=repository,
|
||||
changeset_revision=repository.tip( trans.app ),
|
||||
changeset_revision=repository.tip( app ),
|
||||
repository_clone_url=repository_clone_url,
|
||||
relative_install_dir=repo_dir,
|
||||
repository_files_dir=None,
|
||||
@@ -1969,25 +2011,25 @@ def set_repository_metadata( trans, repository, content_alert_str='', **kwd ):
|
||||
persist=False )
|
||||
if metadata_dict:
|
||||
repository_metadata = None
|
||||
repository_type_class = trans.app.repository_types_registry.get_class_by_label( repository.type )
|
||||
repository_type_class = app.repository_types_registry.get_class_by_label( repository.type )
|
||||
tip_only = isinstance( repository_type_class, TipOnly )
|
||||
if not tip_only and new_metadata_required_for_utilities( trans, repository, metadata_dict ):
|
||||
if not tip_only and new_metadata_required_for_utilities( app, repository, metadata_dict ):
|
||||
# Create a new repository_metadata table row.
|
||||
repository_metadata = create_or_update_repository_metadata( trans,
|
||||
repository_metadata = create_or_update_repository_metadata( app,
|
||||
encoded_id,
|
||||
repository,
|
||||
repository.tip( trans.app ),
|
||||
repository.tip( app ),
|
||||
metadata_dict )
|
||||
# If this is the first record stored for this repository, see if we need to send any email alerts.
|
||||
if len( repository.downloadable_revisions ) == 1:
|
||||
suc.handle_email_alerts( trans, repository, content_alert_str='', new_repo_alert=True, admin_only=False )
|
||||
suc.handle_email_alerts( app, host, repository, content_alert_str='', new_repo_alert=True, admin_only=False )
|
||||
else:
|
||||
# Update the latest stored repository metadata with the contents and attributes of metadata_dict.
|
||||
repository_metadata = get_latest_repository_metadata( trans, repository.id, downloadable=False )
|
||||
repository_metadata = get_latest_repository_metadata( app, repository.id, downloadable=False )
|
||||
if repository_metadata:
|
||||
downloadable = is_downloadable( metadata_dict )
|
||||
# Update the last saved repository_metadata table row.
|
||||
repository_metadata.changeset_revision = repository.tip( trans.app )
|
||||
repository_metadata.changeset_revision = repository.tip( app )
|
||||
repository_metadata.metadata = metadata_dict
|
||||
repository_metadata.downloadable = downloadable
|
||||
if 'datatypes' in metadata_dict:
|
||||
@@ -2018,14 +2060,14 @@ def set_repository_metadata( trans, repository, content_alert_str='', **kwd ):
|
||||
repository_metadata.tools_functionally_correct = False
|
||||
repository_metadata.missing_test_components = False
|
||||
repository_metadata.tool_test_results = None
|
||||
trans.sa_session.add( repository_metadata )
|
||||
trans.sa_session.flush()
|
||||
sa_session.add( repository_metadata )
|
||||
sa_session.flush()
|
||||
else:
|
||||
# There are no metadata records associated with the repository.
|
||||
repository_metadata = create_or_update_repository_metadata( trans,
|
||||
repository_metadata = create_or_update_repository_metadata( app,
|
||||
encoded_id,
|
||||
repository,
|
||||
repository.tip( trans.app ),
|
||||
repository.tip( app ),
|
||||
metadata_dict )
|
||||
if 'tools' in metadata_dict and repository_metadata and status != 'error':
|
||||
# Set tool versions on the new downloadable change set. The order of the list of changesets is
|
||||
@@ -2033,41 +2075,32 @@ def set_repository_metadata( trans, repository, content_alert_str='', **kwd ):
|
||||
changeset_revisions = []
|
||||
for changeset in repo.changelog:
|
||||
changeset_revision = str( repo.changectx( changeset ) )
|
||||
if suc.get_repository_metadata_by_changeset_revision( trans.app, encoded_id, changeset_revision ):
|
||||
if suc.get_repository_metadata_by_changeset_revision( app, encoded_id, changeset_revision ):
|
||||
changeset_revisions.append( changeset_revision )
|
||||
add_tool_versions( trans, encoded_id, repository_metadata, changeset_revisions )
|
||||
add_tool_versions( app, encoded_id, repository_metadata, changeset_revisions )
|
||||
elif len( repo ) == 1 and not invalid_file_tups:
|
||||
message = "Revision <b>%s</b> includes no Galaxy utilities for which metadata can " % str( repository.tip( trans.app ) )
|
||||
message = "Revision <b>%s</b> includes no Galaxy utilities for which metadata can " % str( repository.tip( app ) )
|
||||
message += "be defined so this revision cannot be automatically installed into a local Galaxy instance."
|
||||
status = "error"
|
||||
if invalid_file_tups:
|
||||
message = tool_util.generate_message_for_invalid_tools( trans, invalid_file_tups, repository, metadata_dict )
|
||||
message = tool_util.generate_message_for_invalid_tools( app, invalid_file_tups, repository, metadata_dict )
|
||||
status = 'error'
|
||||
# Reset the tool_data_tables by loading the empty tool_data_table_conf.xml file.
|
||||
tool_util.reset_tool_data_tables( trans.app )
|
||||
tool_util.reset_tool_data_tables( app )
|
||||
return message, status
|
||||
|
||||
def set_repository_metadata_due_to_new_tip( trans, repository, content_alert_str=None, **kwd ):
|
||||
def set_repository_metadata_due_to_new_tip( app, host, user, repository, content_alert_str=None, **kwd ):
|
||||
"""Set metadata on the repository tip in the tool shed."""
|
||||
# This method is not called from Galaxy.
|
||||
error_message, status = set_repository_metadata( trans,
|
||||
repository,
|
||||
content_alert_str=content_alert_str,
|
||||
**kwd )
|
||||
if error_message:
|
||||
# FIXME: This probably should not redirect since this method is called from the upload controller as well
|
||||
# as the repository controller.
|
||||
# If there is an error, display it.
|
||||
return trans.response.send_redirect( web.url_for( controller='repository',
|
||||
action='manage_repository',
|
||||
id=trans.security.encode_id( repository.id ),
|
||||
message=error_message,
|
||||
status='error' ) )
|
||||
error_message, status = set_repository_metadata( app, host, user, repository, content_alert_str=content_alert_str, **kwd )
|
||||
return status, error_message
|
||||
|
||||
def update_existing_tool_dependency( app, repository, original_dependency_dict, new_dependencies_dict ):
|
||||
"""
|
||||
Update an exsiting tool dependency whose definition was updated in a change set pulled by a Galaxy administrator when getting updates
|
||||
to an installed tool shed repository. The original_dependency_dict is a single tool dependency definition, an example of which is::
|
||||
Update an exsiting tool dependency whose definition was updated in a change set
|
||||
pulled by a Galaxy administrator when getting updates to an installed tool shed
|
||||
repository. The original_dependency_dict is a single tool dependency definition,
|
||||
an example of which is::
|
||||
|
||||
{"name": "bwa",
|
||||
"readme": "\\nCompiling BWA requires zlib and libpthread to be present on your system.\\n ",
|
||||
|
||||
@@ -5,7 +5,6 @@ import re
|
||||
import tool_shed.util.shed_util_common as suc
|
||||
from tool_shed.util import common_util
|
||||
from tool_shed.util import hg_util
|
||||
from tool_shed.util import import_util
|
||||
from tool_shed.util import repository_dependency_util
|
||||
from galaxy import util
|
||||
from galaxy import web
|
||||
@@ -38,12 +37,12 @@ def change_repository_name_in_hgrc_file( hgrc_file, new_name ):
|
||||
config.write( new_file )
|
||||
new_file.close()
|
||||
|
||||
def create_hgrc_file( trans, repository ):
|
||||
def create_hgrc_file( app, repository ):
|
||||
# At this point, an entry for the repository is required to be in the hgweb.config file so we can call repository.repo_path( trans.app ).
|
||||
# Since we support both http and https, we set push_ssl to False to override the default (which is True) in the mercurial api. The hg
|
||||
# purge extension purges all files and directories not being tracked by mercurial in the current repository. It'll remove unknown files
|
||||
# and empty directories. This is not currently used because it is not supported in the mercurial API.
|
||||
repo = hg_util.get_repo_for_repository( trans.app, repository=repository, repo_path=None, create=False )
|
||||
repo = hg_util.get_repo_for_repository( app, repository=repository, repo_path=None, create=False )
|
||||
fp = repo.opener( 'hgrc', 'wb' )
|
||||
fp.write( '[paths]\n' )
|
||||
fp.write( 'default = .\n' )
|
||||
@@ -129,20 +128,21 @@ def create_repo_info_dict( app, repository_clone_url, changeset_revision, ctx_re
|
||||
tool_dependencies )
|
||||
return repo_info_dict
|
||||
|
||||
def create_repository( trans, name, type, description, long_description, user_id, category_ids=[] ):
|
||||
def create_repository( app, name, type, description, long_description, user_id, category_ids=[] ):
|
||||
sa_session = app.model.context.current
|
||||
# Add the repository record to the database.
|
||||
repository = trans.app.model.Repository( name=name,
|
||||
type=type,
|
||||
description=description,
|
||||
long_description=long_description,
|
||||
user_id=user_id )
|
||||
repository = app.model.Repository( name=name,
|
||||
type=type,
|
||||
description=description,
|
||||
long_description=long_description,
|
||||
user_id=user_id )
|
||||
# Flush to get the id.
|
||||
trans.sa_session.add( repository )
|
||||
trans.sa_session.flush()
|
||||
sa_session.add( repository )
|
||||
sa_session.flush()
|
||||
# Create an admin role for the repository.
|
||||
repository_admin_role = create_repository_admin_role( trans, repository )
|
||||
repository_admin_role = create_repository_admin_role( app, repository )
|
||||
# Determine the repository's repo_path on disk.
|
||||
dir = os.path.join( trans.app.config.file_path, *directory_hash_id( repository.id ) )
|
||||
dir = os.path.join( app.config.file_path, *directory_hash_id( repository.id ) )
|
||||
# Create directory if it does not exist.
|
||||
if not os.path.exists( dir ):
|
||||
os.makedirs( dir )
|
||||
@@ -152,112 +152,47 @@ def create_repository( trans, name, type, description, long_description, user_id
|
||||
if not os.path.exists( repository_path ):
|
||||
os.makedirs( repository_path )
|
||||
# Create the local repository.
|
||||
repo = hg_util.get_repo_for_repository( trans.app, repository=None, repo_path=repository_path, create=True )
|
||||
repo = hg_util.get_repo_for_repository( app, repository=None, repo_path=repository_path, create=True )
|
||||
# Add an entry in the hgweb.config file for the local repository.
|
||||
lhs = "repos/%s/%s" % ( repository.user.username, repository.name )
|
||||
trans.app.hgweb_config_manager.add_entry( lhs, repository_path )
|
||||
app.hgweb_config_manager.add_entry( lhs, repository_path )
|
||||
# Create a .hg/hgrc file for the local repository.
|
||||
create_hgrc_file( trans, repository )
|
||||
create_hgrc_file( app, repository )
|
||||
flush_needed = False
|
||||
if category_ids:
|
||||
# Create category associations
|
||||
for category_id in category_ids:
|
||||
category = trans.sa_session.query( trans.model.Category ) \
|
||||
.get( trans.security.decode_id( category_id ) )
|
||||
rca = trans.app.model.RepositoryCategoryAssociation( repository, category )
|
||||
trans.sa_session.add( rca )
|
||||
category = sa_session.query( app.model.Category ) \
|
||||
.get( app.security.decode_id( category_id ) )
|
||||
rca = app.model.RepositoryCategoryAssociation( repository, category )
|
||||
sa_session.add( rca )
|
||||
flush_needed = True
|
||||
if flush_needed:
|
||||
trans.sa_session.flush()
|
||||
sa_session.flush()
|
||||
# Update the repository registry.
|
||||
trans.app.repository_registry.add_entry( repository )
|
||||
app.repository_registry.add_entry( repository )
|
||||
message = "Repository <b>%s</b> has been created." % str( repository.name )
|
||||
return repository, message
|
||||
|
||||
def create_repository_admin_role( trans, repository ):
|
||||
def create_repository_admin_role( app, repository ):
|
||||
"""
|
||||
Create a new role with name-spaced name based on the repository name and its owner's public user
|
||||
name. This will ensure that the tole name is unique.
|
||||
"""
|
||||
sa_session = app.model.context.current
|
||||
name = get_repository_admin_role_name( str( repository.name ), str( repository.user.username ) )
|
||||
description = 'A user or group member with this role can administer this repository.'
|
||||
role = trans.app.model.Role( name=name, description=description, type=trans.app.model.Role.types.SYSTEM )
|
||||
trans.sa_session.add( role )
|
||||
trans.sa_session.flush()
|
||||
role = app.model.Role( name=name, description=description, type=app.model.Role.types.SYSTEM )
|
||||
sa_session.add( role )
|
||||
sa_session.flush()
|
||||
# Associate the role with the repository owner.
|
||||
ura = trans.model.UserRoleAssociation( repository.user, role )
|
||||
ura = app.model.UserRoleAssociation( repository.user, role )
|
||||
# Associate the role with the repository.
|
||||
rra = trans.model.RepositoryRoleAssociation( repository, role )
|
||||
trans.sa_session.add( rra )
|
||||
trans.sa_session.flush()
|
||||
rra = app.model.RepositoryRoleAssociation( repository, role )
|
||||
sa_session.add( rra )
|
||||
sa_session.flush()
|
||||
return role
|
||||
|
||||
def create_repository_and_import_archive( trans, repository_archive_dict, import_results_tups ):
|
||||
"""
|
||||
Create a new repository in the tool shed and populate it with the contents of a gzip compressed
|
||||
tar archive that was exported as part or all of the contents of a capsule.
|
||||
"""
|
||||
results_message = ''
|
||||
name = repository_archive_dict.get( 'name', None )
|
||||
username = repository_archive_dict.get( 'owner', None )
|
||||
if name is None or username is None:
|
||||
ok = False
|
||||
results_message += 'Import failed: required repository name <b>%s</b> or owner <b>%s</b> is missing.' % \
|
||||
( str( name ), str( username ))
|
||||
import_results_tups.append( ( ok, ( str( name ), str( username ) ), results_message ) )
|
||||
else:
|
||||
status = repository_archive_dict.get( 'status', None )
|
||||
if status is None:
|
||||
# The repository does not yet exist in this Tool Shed and the current user is authorized to import
|
||||
# the current archive file.
|
||||
type = repository_archive_dict.get( 'type', 'unrestricted' )
|
||||
description = repository_archive_dict.get( 'description', '' )
|
||||
long_description = repository_archive_dict.get( 'long_description', '' )
|
||||
# The owner entry in the repository_archive_dict is the public username of the user associated with
|
||||
# the exported repository archive.
|
||||
user = suc.get_user_by_username( trans.app, username )
|
||||
if user is None:
|
||||
ok = False
|
||||
results_message += 'Import failed: repository owner <b>%s</b> does not have an account in this Tool Shed.' % str( username )
|
||||
import_results_tups.append( ( ok, ( str( name ), str( username ) ), results_message ) )
|
||||
else:
|
||||
user_id = user.id
|
||||
# The categories entry in the repository_archive_dict is a list of category names. If a name does not
|
||||
# exist in the current Tool Shed, the category will not be created, so it will not be associated with
|
||||
# the repository.
|
||||
category_ids = []
|
||||
category_names = repository_archive_dict.get( 'category_names', [] )
|
||||
for category_name in category_names:
|
||||
category = suc.get_category_by_name( trans.app, category_name )
|
||||
if category is None:
|
||||
results_message += 'This Tool Shed does not have the category <b>%s</b> so it ' % str( category_name )
|
||||
results_message += 'will not be associated with this repository.'
|
||||
else:
|
||||
category_ids.append( trans.security.encode_id( category.id ) )
|
||||
# Create the repository record in the database.
|
||||
repository, create_message = create_repository( trans,
|
||||
name,
|
||||
type,
|
||||
description,
|
||||
long_description,
|
||||
user_id=user_id,
|
||||
category_ids=category_ids )
|
||||
if create_message:
|
||||
results_message += create_message
|
||||
# Populate the new repository with the contents of exported repository archive.
|
||||
results_dict = import_util.import_repository_archive( trans, repository, repository_archive_dict )
|
||||
ok = results_dict.get( 'ok', False )
|
||||
error_message = results_dict.get( 'error_message', '' )
|
||||
if error_message:
|
||||
results_message += error_message
|
||||
import_results_tups.append( ( ok, ( str( name ), str( username ) ), results_message ) )
|
||||
else:
|
||||
# The repository either already exists in this Tool Shed or the current user is not authorized to create it.
|
||||
ok = True
|
||||
results_message += 'Import not necessary: repository status for this Tool Shed is: %s.' % str( status )
|
||||
import_results_tups.append( ( ok, ( str( name ), str( username ) ), results_message ) )
|
||||
return import_results_tups
|
||||
|
||||
def get_repo_info_dict( app, user, repository_id, changeset_revision ):
|
||||
repository = suc.get_repository_in_tool_shed( app, repository_id )
|
||||
repo = hg_util.get_repo_for_repository( app, repository=repository, repo_path=None, create=False )
|
||||
@@ -317,17 +252,19 @@ def get_repo_info_dict( app, user, repository_id, changeset_revision ):
|
||||
def get_repository_admin_role_name( repository_name, repository_owner ):
|
||||
return '%s_%s_admin' % ( str( repository_name ), str( repository_owner ) )
|
||||
|
||||
def get_role_by_id( trans, role_id ):
|
||||
def get_role_by_id( app, role_id ):
|
||||
"""Get a Role from the database by id."""
|
||||
return trans.sa_session.query( trans.model.Role ).get( trans.security.decode_id( role_id ) )
|
||||
sa_session = app.model.context.current
|
||||
return sa_session.query( app.model.Role ).get( app.security.decode_id( role_id ) )
|
||||
|
||||
def handle_role_associations( trans, role, repository, **kwd ):
|
||||
def handle_role_associations( app, role, repository, **kwd ):
|
||||
sa_session = app.model.context.current
|
||||
message = kwd.get( 'message', '' )
|
||||
status = kwd.get( 'status', 'done' )
|
||||
repository_owner = repository.user
|
||||
if kwd.get( 'manage_role_associations_button', False ):
|
||||
in_users_list = util.listify( kwd.get( 'in_users', [] ) )
|
||||
in_users = [ trans.sa_session.query( trans.app.model.User ).get( x ) for x in in_users_list ]
|
||||
in_users = [ sa_session.query( app.model.User ).get( x ) for x in in_users_list ]
|
||||
# Make sure the repository owner is always associated with the repostory's admin role.
|
||||
owner_associated = False
|
||||
for user in in_users:
|
||||
@@ -339,29 +276,29 @@ def handle_role_associations( trans, role, repository, **kwd ):
|
||||
message += "The repository owner must always be associated with the repository's administrator role. "
|
||||
status = 'error'
|
||||
in_groups_list = util.listify( kwd.get( 'in_groups', [] ) )
|
||||
in_groups = [ trans.sa_session.query( trans.app.model.Group ).get( x ) for x in in_groups_list ]
|
||||
in_groups = [ sa_session.query( app.model.Group ).get( x ) for x in in_groups_list ]
|
||||
in_repositories = [ repository ]
|
||||
trans.app.security_agent.set_entity_role_associations( roles=[ role ],
|
||||
app.security_agent.set_entity_role_associations( roles=[ role ],
|
||||
users=in_users,
|
||||
groups=in_groups,
|
||||
repositories=in_repositories )
|
||||
trans.sa_session.refresh( role )
|
||||
sa_session.refresh( role )
|
||||
message += "Role <b>%s</b> has been associated with %d users, %d groups and %d repositories. " % \
|
||||
( str( role.name ), len( in_users ), len( in_groups ), len( in_repositories ) )
|
||||
in_users = []
|
||||
out_users = []
|
||||
in_groups = []
|
||||
out_groups = []
|
||||
for user in trans.sa_session.query( trans.app.model.User ) \
|
||||
.filter( trans.app.model.User.table.c.deleted==False ) \
|
||||
.order_by( trans.app.model.User.table.c.email ):
|
||||
for user in sa_session.query( app.model.User ) \
|
||||
.filter( app.model.User.table.c.deleted==False ) \
|
||||
.order_by( app.model.User.table.c.email ):
|
||||
if user in [ x.user for x in role.users ]:
|
||||
in_users.append( ( user.id, user.email ) )
|
||||
else:
|
||||
out_users.append( ( user.id, user.email ) )
|
||||
for group in trans.sa_session.query( trans.app.model.Group ) \
|
||||
.filter( trans.app.model.Group.table.c.deleted==False ) \
|
||||
.order_by( trans.app.model.Group.table.c.name ):
|
||||
for group in sa_session.query( app.model.Group ) \
|
||||
.filter( app.model.Group.table.c.deleted==False ) \
|
||||
.order_by( app.model.Group.table.c.name ):
|
||||
if group in [ x.group for x in role.groups ]:
|
||||
in_groups.append( ( group.id, group.name ) )
|
||||
else:
|
||||
|
||||
@@ -1231,7 +1231,7 @@ def get_user_by_username( app, username ):
|
||||
except Exception, e:
|
||||
return None
|
||||
|
||||
def handle_email_alerts( trans, repository, content_alert_str='', new_repo_alert=False, admin_only=False ):
|
||||
def handle_email_alerts( app, host, repository, content_alert_str='', new_repo_alert=False, admin_only=False ):
|
||||
"""
|
||||
There are 2 complementary features that enable a tool shed user to receive email notification:
|
||||
1. Within User Preferences, they can elect to receive email when the first (or first valid)
|
||||
@@ -1254,17 +1254,18 @@ def handle_email_alerts( trans, repository, content_alert_str='', new_repo_alert
|
||||
user is not an admin user, the email will not include any information about both HTML and image content
|
||||
that was included in the change set.
|
||||
"""
|
||||
repo = hg_util.get_repo_for_repository( trans.app, repository=repository, repo_path=None, create=False )
|
||||
sa_session = app.model.context.current
|
||||
repo = hg_util.get_repo_for_repository( app, repository=repository, repo_path=None, create=False )
|
||||
sharable_link = generate_sharable_link_for_repository_in_tool_shed( repository, changeset_revision=None )
|
||||
smtp_server = trans.app.config.smtp_server
|
||||
smtp_server = app.config.smtp_server
|
||||
if smtp_server and ( new_repo_alert or repository.email_alerts ):
|
||||
# Send email alert to users that want them.
|
||||
if trans.app.config.email_from is not None:
|
||||
email_from = trans.app.config.email_from
|
||||
elif trans.request.host.split( ':' )[0] == 'localhost':
|
||||
if app.config.email_from is not None:
|
||||
email_from = app.config.email_from
|
||||
elif host.split( ':' )[0] == 'localhost':
|
||||
email_from = 'galaxy-no-reply@' + socket.getfqdn()
|
||||
else:
|
||||
email_from = 'galaxy-no-reply@' + trans.request.host.split( ':' )[0]
|
||||
email_from = 'galaxy-no-reply@' + host.split( ':' )[0]
|
||||
tip_changeset = repo.changelog.tip()
|
||||
ctx = repo.changectx( tip_changeset )
|
||||
try:
|
||||
@@ -1278,7 +1279,7 @@ def handle_email_alerts( trans, repository, content_alert_str='', new_repo_alert
|
||||
else:
|
||||
template = email_alert_template
|
||||
display_date = hg_util.get_readable_ctx_date( ctx )
|
||||
admin_body = string.Template( template ).safe_substitute( host=trans.request.host,
|
||||
admin_body = string.Template( template ).safe_substitute( host=host,
|
||||
sharable_link=sharable_link,
|
||||
repository_name=repository.name,
|
||||
revision='%s:%s' %( str( ctx.rev() ), ctx ),
|
||||
@@ -1286,7 +1287,7 @@ def handle_email_alerts( trans, repository, content_alert_str='', new_repo_alert
|
||||
description=ctx.description(),
|
||||
username=username,
|
||||
content_alert_str=content_alert_str )
|
||||
body = string.Template( template ).safe_substitute( host=trans.request.host,
|
||||
body = string.Template( template ).safe_substitute( host=host,
|
||||
sharable_link=sharable_link,
|
||||
repository_name=repository.name,
|
||||
revision='%s:%s' %( str( ctx.rev() ), ctx ),
|
||||
@@ -1294,15 +1295,15 @@ def handle_email_alerts( trans, repository, content_alert_str='', new_repo_alert
|
||||
description=ctx.description(),
|
||||
username=username,
|
||||
content_alert_str='' )
|
||||
admin_users = trans.app.config.get( "admin_users", "" ).split( "," )
|
||||
admin_users = app.config.get( "admin_users", "" ).split( "," )
|
||||
frm = email_from
|
||||
if new_repo_alert:
|
||||
subject = "Galaxy tool shed alert for new repository named %s" % str( repository.name )
|
||||
subject = subject[ :80 ]
|
||||
email_alerts = []
|
||||
for user in trans.sa_session.query( trans.model.User ) \
|
||||
.filter( and_( trans.model.User.table.c.deleted == False,
|
||||
trans.model.User.table.c.new_repo_alert == True ) ):
|
||||
for user in sa_session.query( app.model.User ) \
|
||||
.filter( and_( app.model.User.table.c.deleted == False,
|
||||
app.model.User.table.c.new_repo_alert == True ) ):
|
||||
if admin_only:
|
||||
if user.email in admin_users:
|
||||
email_alerts.append( user.email )
|
||||
@@ -1316,9 +1317,9 @@ def handle_email_alerts( trans, repository, content_alert_str='', new_repo_alert
|
||||
# Send it
|
||||
try:
|
||||
if to in admin_users:
|
||||
util.send_mail( frm, to, subject, admin_body, trans.app.config )
|
||||
util.send_mail( frm, to, subject, admin_body, app.config )
|
||||
else:
|
||||
util.send_mail( frm, to, subject, body, trans.app.config )
|
||||
util.send_mail( frm, to, subject, body, app.config )
|
||||
except Exception, e:
|
||||
log.exception( "An error occurred sending a tool shed repository update alert by email." )
|
||||
|
||||
|
||||
@@ -239,7 +239,7 @@ def copy_sample_files( app, sample_files, tool_path=None, sample_files_copied=No
|
||||
if is_data_index_sample_file( filename ):
|
||||
copy_sample_file( app, filename, dest_path=dest_path )
|
||||
|
||||
def generate_message_for_invalid_tools( trans, invalid_file_tups, repository, metadata_dict, as_html=True, displaying_invalid_tool=False ):
|
||||
def generate_message_for_invalid_tools( app, invalid_file_tups, repository, metadata_dict, as_html=True, displaying_invalid_tool=False ):
|
||||
if as_html:
|
||||
new_line = '<br/>'
|
||||
bold_start = '<b>'
|
||||
@@ -249,10 +249,10 @@ def generate_message_for_invalid_tools( trans, invalid_file_tups, repository, me
|
||||
bold_start = ''
|
||||
bold_end = ''
|
||||
message = ''
|
||||
if trans.webapp.name == 'galaxy':
|
||||
if app.name == 'galaxy':
|
||||
tip_rev = str( repository.changeset_revision )
|
||||
else:
|
||||
tip_rev = str( repository.tip( trans.app ) )
|
||||
tip_rev = str( repository.tip( app ) )
|
||||
if not displaying_invalid_tool:
|
||||
if metadata_dict:
|
||||
message += "Metadata may have been defined for some items in revision '%s'. " % tip_rev
|
||||
@@ -883,7 +883,7 @@ def load_tool_from_changeset_revision( trans, repository_id, changeset_revision,
|
||||
tool,
|
||||
sample_files )
|
||||
if invalid_files_and_errors_tups:
|
||||
message2 = generate_message_for_invalid_tools( trans,
|
||||
message2 = generate_message_for_invalid_tools( trans.app,
|
||||
invalid_files_and_errors_tups,
|
||||
repository,
|
||||
metadata_dict=None,
|
||||
|
||||
Reference in New Issue
Block a user