From 36177396c40d2a91a5985ac12f8d1e15a850695f Mon Sep 17 00:00:00 2001 From: Will Thames Date: Wed, 13 Aug 2014 21:32:36 +1000 Subject: [PATCH] Allow ansible-galaxy to install roles from URLs ansible-galaxy can now refer to SCM URLs (git and hg at this point) for role names Dependencies need to use the full SCM URLs too. Otherwise all seems to work well Test rolesfile ``` http://bitbucket.org/willthames/git-ansible-galaxy,v1.1 https://bitbucket.org/willthames/hg-ansible-galaxy ``` (works with ssh too) --- bin/ansible-galaxy | 120 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 94 insertions(+), 26 deletions(-) diff --git a/bin/ansible-galaxy b/bin/ansible-galaxy index 0d173321fc..09db0bcff2 100755 --- a/bin/ansible-galaxy +++ b/bin/ansible-galaxy @@ -26,6 +26,7 @@ import json import os import os.path import shutil +import subprocess import sys import tarfile import tempfile @@ -326,6 +327,63 @@ def api_get_list(api_server, what): print " - failed to download the %s list" % what return None +#------------------------------------------------------------------------------------- +# scm repo utility functions +#------------------------------------------------------------------------------------- + +def repo_fetch_role(role_name, role_version): + check_repo_cmd = { 'git': ['git', 'ls-remote', role_name], + 'hg': ['hg', 'identify', role_name]} + with open('/dev/null', 'w') as devnull: + for (scm, cmd) in check_repo_cmd.items(): + popen = subprocess.Popen(cmd, stdout=devnull, stderr=devnull) + rc = popen.wait() + if rc == 0: + return scm_archive_role(scm, role_name, role_version) + + print "Repo doesn't seem to be hg or git" + sys.exit(2) + + +def scm_archive_role(scm, role_url, role_version): + tempdir = tempfile.mkdtemp() + role_name = role_url.split('/')[-1] + clone_cmd = [scm, 'clone', role_url] + with open('/dev/null', 'w') as devnull: + popen = subprocess.Popen(clone_cmd, cwd=tempdir, stdout=devnull, stderr=devnull) + rc = popen.wait() + if rc != 0: + print "Command %s failed" % ' '.join(clone_cmd) + print "in directory %s" % temp_dir + sys.exit(1) + + temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.tar.gz') + if scm == 'hg': + archive_cmd = ['hg', 'archive', '--prefix', "%s/" % role_name] + if role_version: + archive_cmd.extend(['-r', role_version]) + archive_cmd.append(temp_file.name) + if scm == 'git': + archive_cmd = ['git', 'archive', '--prefix=%s/' % role_name, '--output=%s' % temp_file.name] + if role_version: + archive_cmd.append(role_version) + else: + archive_cmd.append('HEAD') + + with open('/dev/null', 'w') as devnull: + popen = subprocess.Popen(archive_cmd, cwd=os.path.join(tempdir, role_name), + stderr=devnull, stdout=devnull) + rc = popen.wait() + if rc != 0: + print "Command %s failed" % ' '.join(archive_cmd) + print "in directory %s" % tempdir + sys.exit(1) + + shutil.rmtree(tempdir) + + return temp_file.name + + #------------------------------------------------------------------------------------- # Role utility functions #------------------------------------------------------------------------------------- @@ -680,40 +738,50 @@ def execute_install(args, options, parser): print "%s (%s) was NOT installed successfully." % (role_name,tar_file) exit_without_ignore(options) else: - # installing remotely - role_data = api_lookup_role_by_name(api_server, role_name) - if not role_data: - print "Sorry, %s was not found on %s." % (role_name, api_server) - continue - - role_versions = api_fetch_role_related(api_server, 'versions', role_data['id']) - if not role_version: - # convert the version names to LooseVersion objects - # and sort them to get the latest version. If there - # are no versions in the list, we'll grab the head - # of the master branch - if len(role_versions) > 0: - loose_versions = [LooseVersion(a.get('name',None)) for a in role_versions] - loose_versions.sort() - role_version = str(loose_versions[-1]) - else: - role_version = 'master' - print " no version specified, installing %s" % role_version + if '://' in role_name: + # installing from scm url + tmp_file = repo_fetch_role(role_name, role_version) + role_name = role_name.split('/')[-1] + role_data = None else: - if role_versions and role_version not in [a.get('name',None) for a in role_versions]: - print "The specified version (%s) was not found in the list of available versions." % role_version - exit_without_ignore(options) + # installing remotely + role_data = api_lookup_role_by_name(api_server, role_name) + if not role_data: + print "Sorry, %s was not found on %s." % (role_name, api_server) continue - # download the role. if --no-deps was specified, we stop here, - # otherwise we recursively grab roles and all of their deps. - tmp_file = fetch_role(role_name, role_version, role_data, options) + role_versions = api_fetch_role_related(api_server, 'versions', role_data['id']) + if not role_version: + # convert the version names to LooseVersion objects + # and sort them to get the latest version. If there + # are no versions in the list, we'll grab the head + # of the master branch + if len(role_versions) > 0: + loose_versions = [LooseVersion(a.get('name',None)) for a in role_versions] + loose_versions.sort() + role_version = str(loose_versions[-1]) + else: + role_version = 'master' + print " no version specified, installing %s" % role_version + else: + if role_versions and role_version not in [a.get('name',None) for a in role_versions]: + print "The specified version (%s) was not found in the list of available versions." % role_version + exit_without_ignore(options) + continue + + # download the role. if --no-deps was specified, we stop here, + # otherwise we recursively grab roles and all of their deps. + tmp_file = fetch_role(role_name, role_version, role_data, options) if tmp_file and install_role(role_name, role_version, tmp_file, options): # we're done with the temp file, clean it up os.unlink(tmp_file) # install dependencies, if we want them if not no_deps: - role_dependencies = role_data['summary_fields']['dependencies'] # api_fetch_role_related(api_server, 'dependencies', role_data['id']) + if not role_data: + role_data = get_role_metadata(role_name, options) + role_dependencies = role_data['dependencies'] + else: + role_dependencies = role_data['summary_fields']['dependencies'] # api_fetch_role_related(api_server, 'dependencies', role_data['id']) for dep_name in role_dependencies: #dep_name = "%s.%s" % (dep['owner'], dep['name']) if not get_role_metadata(dep_name, options):