diff --git a/bin/ansible-galaxy b/bin/ansible-galaxy index afc2d9de85..3c624693b3 100755 --- a/bin/ansible-galaxy +++ b/bin/ansible-galaxy @@ -696,10 +696,12 @@ def execute_install(args, options, parser): roles_done = [] if role_file: - # roles listed in a file, one per line - # so we'll go through and grab them all f = open(role_file, 'r') - roles_left = f.readlines() + if role_file.endswith('.yaml') or role_file.endswith('.yml'): + roles_left = map(ansible.utils.role_yaml_parse, yaml.safe_load(f)) + else: + # roles listed in a file, one per line + roles_left = map(ansible.utils.role_spec_parse, f.readlines()) f.close() else: # roles were specified directly, so we'll just go out grab them @@ -708,16 +710,18 @@ def execute_install(args, options, parser): while len(roles_left) > 0: # query the galaxy API for the role data - (scm, role_src, role_version, role_name) = ansible.utils.role_spec_parse(roles_left.pop(0)) role_data = None + role = roles_left.pop(0) + role_src = role.get("src") + role_scm = role.get("scm") if os.path.isfile(role_src): # installing a local tar.gz tmp_file = role_src else: - if scm: + if role_scm: # create tar file from scm url - tmp_file = scm_archive_role(scm, role_src, role_version, role_name) + tmp_file = scm_archive_role(role_scm, role_src, role.get("version"), role.get("name")) elif '://' in role_src: # just download a URL - version will probably be in the URL tmp_file = fetch_role(role_src, None, None, options) @@ -729,7 +733,7 @@ def execute_install(args, options, parser): continue role_versions = api_fetch_role_related(api_server, 'versions', role_data['id']) - if not role_version: + if "version" not in role: # 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 @@ -737,40 +741,43 @@ def execute_install(args, options, parser): 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]) + role["version"] = str(loose_versions[-1]) else: - role_version = 'master' - print " no version specified, installing %s" % role_version + 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 + 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_src, role_version, role_data, options) - if tmp_file and install_role(role_name, role_version, tmp_file, options): + tmp_file = fetch_role(role_src, role["version"], role_data, options) + if tmp_file and install_role(role.get("name"), role.get("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: if not role_data: - role_data = get_role_metadata(role_name, options) + role_data = get_role_metadata(role.get("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.split('/')[-1], options): - print ' adding dependency: %s' % dep_name - roles_left.append(dep_name) + for dep in role_dependencies: + if isinstance(dep, str): + dep = ansible.utils.role_spec_parse(dep) else: - print ' dependency %s is already installed, skipping.' % dep_name + dep = ansible.utils.role_yaml_parse(dep) + if not get_role_metadata(dep["name"], options): + print ' adding dependency: %s' % dep["name"] + roles_left.append(dep) + else: + print ' dependency %s is already installed, skipping.' % dep["name"] else: if tmp_file: os.unlink(tmp_file) - print "%s was NOT installed successfully." % role_name + print "%s was NOT installed successfully." % role.get("name") exit_without_ignore(options) sys.exit(0) diff --git a/docsite/rst/playbooks_roles.rst b/docsite/rst/playbooks_roles.rst index 233ba9becf..4ce69a7130 100644 --- a/docsite/rst/playbooks_roles.rst +++ b/docsite/rst/playbooks_roles.rst @@ -299,6 +299,13 @@ Role dependencies can also be specified as a full path, just like top level role dependencies: - { role: '/path/to/common/roles/foo', x: 1 } +Role dependencies can also be installed from source control repos or tar files, using a comma separated format of path, an optional version (tag, commit, branch etc) and optional friendly role name (an attempt is made to derive a role name from the repo name or archive filename):: + + --- + dependencies: + - { role: 'git+http://git.example.com/repos/role-foo,v1.1,foo' } + - { role: '/path/to/tar/file.tgz,,friendly-name' } + Roles dependencies are always executed before the role that includes them, and are recursive. By default, roles can also only be added as a dependency once - if another role also lists it as a dependency it will not be run again. This behavior can be overridden by adding `allow_duplicates: yes` to the `meta/main.yml` file. diff --git a/lib/ansible/playbook/play.py b/lib/ansible/playbook/play.py index cdbab8c59e..50bd99173c 100644 --- a/lib/ansible/playbook/play.py +++ b/lib/ansible/playbook/play.py @@ -187,7 +187,7 @@ class Play(object): raise errors.AnsibleError("expected a role name in dictionary: %s" % orig_path) role_vars = orig_path else: - (scm, role_src, role_version, role_name) = utils.role_spec_parse(orig_path) + role_name = utils.role_spec_parse(orig_path)["name"] role_path = None @@ -412,8 +412,7 @@ class Play(object): if isinstance(role, dict): role_name = role['role'] else: - role_name = role - (scm, role_src, role_version, role_name) = utils.role_spec_parse(role_name) + role_name = utils.role_spec_parse(role)["name"] role_names.append(role_name) if os.path.isfile(task): diff --git a/lib/ansible/utils/__init__.py b/lib/ansible/utils/__init__.py index 65dc80150b..5692d6986b 100644 --- a/lib/ansible/utils/__init__.py +++ b/lib/ansible/utils/__init__.py @@ -380,7 +380,17 @@ def role_spec_parse(role_spec): role_name = tokens[2] else: role_name = repo_url_to_role_name(tokens[0]) - return (scm, role_url, role_version, role_name) + return dict(scm=scm, src=role_url, version=role_version, name=role_name) + + +def role_yaml_parse(role): + if '+' in role["src"]: + (scm, src) = role["src"].split('+') + role["scm"] = scm + role["src"] = src + if 'name' not in role: + role["name"] = repo_url_to_role_name(role["src"]) + return role def json_loads(data): diff --git a/test/integration/Makefile b/test/integration/Makefile index e31b820e8b..8188680c50 100644 --- a/test/integration/Makefile +++ b/test/integration/Makefile @@ -105,11 +105,22 @@ rackspace: $(CREDENTIALS_FILE) CLOUD_RESOURCE_PREFIX="$(CLOUD_RESOURCE_PREFIX)" make rackspace_cleanup ; \ exit $$RC; -test_galaxy: +test_galaxy: test_galaxy_spec test_galaxy_yaml + +test_galaxy_spec: mytmpdir=$(TMPDIR) ; \ ansible-galaxy install -r galaxy_rolesfile -p $$mytmpdir/roles ; \ cp galaxy_playbook.yml $$mytmpdir ; \ ansible-playbook -i $(INVENTORY) $$mytmpdir/galaxy_playbook.yml -v $(TEST_FLAGS) ; \ RC=$$? ; \ rm -rf $$mytmpdir ; \ - exit $$RC + exit $$RC + +test_galaxy_yaml: + mytmpdir=$(TMPDIR) ; \ + ansible-galaxy install -r galaxy_roles.yml -p $$mytmpdir/roles ; \ + cp galaxy_playbook.yml $$mytmpdir ; \ + ansible-playbook -i $(INVENTORY) $$mytmpdir/galaxy_playbook.yml -v $(TEST_FLAGS) ; \ + RC=$$? ; \ + rm -rf $$mytmpdir ; \ + exit $$RC diff --git a/test/integration/galaxy_playbook.yml b/test/integration/galaxy_playbook.yml index d7adebf340..8e64798c70 100644 --- a/test/integration/galaxy_playbook.yml +++ b/test/integration/galaxy_playbook.yml @@ -4,3 +4,4 @@ roles: - "git-ansible-galaxy" - "http-role" + - "hg-ansible-galaxy" diff --git a/test/integration/galaxy_roles.yml b/test/integration/galaxy_roles.yml new file mode 100644 index 0000000000..f354f5985f --- /dev/null +++ b/test/integration/galaxy_roles.yml @@ -0,0 +1,8 @@ +- src: git+http://bitbucket.org/willthames/git-ansible-galaxy + version: v1.4 + +- src: ssh://hg@bitbucket.org/willthames/hg-ansible-galaxy + scm: hg + +- src: https://bitbucket.org/willthames/http-ansible-galaxy/get/master.tar.gz + name: http-role