Merge "Convert zuul.projects to a dict" into feature/zuulv3
diff --git a/doc/source/user/jobs.rst b/doc/source/user/jobs.rst
index 989338a..ae720d8 100644
--- a/doc/source/user/jobs.rst
+++ b/doc/source/user/jobs.rst
@@ -220,14 +220,15 @@
          `src/git.example.com/org/project`.
 
    .. var:: projects
-      :type: list
+      :type: dict
 
       A list of all projects prepared by Zuul for the item.  It
       includes, at least, the item's own project.  It also includes
       the projects of any items this item depends on, as well as the
       projects that appear in :attr:`job.required-projects`.
 
-      This is a list of dictionaries, with each element consisting of:
+      This is a dictionary of dictionaries.  Each value has a key of
+      the `canonical_name`, then each entry consists of:
 
       .. var:: name
 
@@ -264,6 +265,20 @@
          This may be influenced by the branch or tag associated with
          the item as well as the job configuration.
 
+      For example, to access the source directory of a single known
+      project, you might use::
+
+        {{ zuul.projects['git.example.com/org/project'].src_dir }}
+
+      To iterate over the project list, you might write a task
+      something like::
+
+        - name: Sample project iteration
+          debug:
+            msg: "Project {{ item.name }} is at {{ item.src_dir }}
+          with_items: {{ zuul.projects.values() | list }}
+
+
    .. var:: _projects
       :type: dict
 
diff --git a/zuul/executor/client.py b/zuul/executor/client.py
index a8b94f0..06c2087 100644
--- a/zuul/executor/client.py
+++ b/zuul/executor/client.py
@@ -180,8 +180,7 @@
         if (hasattr(item.change, 'newrev') and item.change.newrev
             and item.change.newrev != '0' * 40):
             zuul_params['newrev'] = item.change.newrev
-        zuul_params['projects'] = []  # Set below
-        zuul_params['_projects'] = {}  # transitional to convert to dict
+        zuul_params['projects'] = {}  # Set below
         zuul_params['items'] = dependent_changes
 
         params = dict()
@@ -253,7 +252,7 @@
                 params['projects'].append(make_project_dict(project))
                 projects.add(project)
         for p in projects:
-            zuul_params['_projects'][p.canonical_name] = (dict(
+            zuul_params['projects'][p.canonical_name] = (dict(
                 name=p.name,
                 short_name=p.name.split('/')[-1],
                 # Duplicate this into the dict too, so that iterating
@@ -265,12 +264,10 @@
             ))
         # We are transitioning "projects" from a list to a dict
         # indexed by canonical name, as it is much easier to access
-        # values in ansible.  Existing callers are converted to
-        # "_projects", then once "projects" is unused we switch it,
-        # then convert callers back.  Finally when "_projects" is
-        # unused it will be removed.
-        for cn, p in zuul_params['_projects'].items():
-            zuul_params['projects'].append(p)
+        # values in ansible.  Existing callers have been converted to
+        # "_projects" and "projects" is swapped; we will convert users
+        # back to "projects" and remove this soon.
+        zuul_params['_projects'] = zuul_params['projects']
 
         build = Build(job, uuid)
         build.parameters = params