checkout: Ignore excluded submodules

Ignore excluded submodules when choosing where to apply a change.

Bug: b/325661630
Change-Id: I8e06f2fc94b29f0ebf68d8299ad1c3383e236e02
Reviewed-on: https://pigweed-review.googlesource.com/c/infra/recipes/+/193370
Presubmit-Verified: CQ Bot Account <pigweed-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Ted Pudlik <tpudlik@google.com>
Pigweed-Auto-Submit: Rob Mohr <mohrr@google.com>
Commit-Queue: Auto-Submit <auto-submit@pigweed-service-accounts.iam.gserviceaccount.com>
diff --git a/recipe_modules/checkout/api.py b/recipe_modules/checkout/api.py
index e4f3d0c..543419c 100644
--- a/recipe_modules/checkout/api.py
+++ b/recipe_modules/checkout/api.py
@@ -1102,10 +1102,11 @@
                 for change in ctx.changes:
                     matching_submodules = []
                     for submodule in submodules:
-                        if ctx.remotes_equivalent(
-                            submodule.remote, change.remote
-                        ):
-                            matching_submodules.append(submodule)
+                        if submodule.initialized:
+                            if ctx.remotes_equivalent(
+                                submodule.remote, change.remote
+                            ):
+                                matching_submodules.append(submodule)
 
                     if not matching_submodules:
                         continue
diff --git a/recipe_modules/checkout/resources/submodule_status.py b/recipe_modules/checkout/resources/submodule_status.py
index 076c42a..677c6be 100755
--- a/recipe_modules/checkout/resources/submodule_status.py
+++ b/recipe_modules/checkout/resources/submodule_status.py
@@ -192,6 +192,7 @@
 @trace
 def main(git_root, output_file, recursive):
     data = {}
+    git_root = git_root.resolve()
     _parse_gitmodules(git_root, data, recursive)
     _add_remotes(git_root, data)
     _add_status(git_root, data, recursive)
diff --git a/recipe_modules/checkout/tests/submodule.expected/submodule-ci.json b/recipe_modules/checkout/tests/submodule.expected/submodule-ci.json
index 17b1f6e..11ef381 100644
--- a/recipe_modules/checkout/tests/submodule.expected/submodule-ci.json
+++ b/recipe_modules/checkout/tests/submodule.expected/submodule-ci.json
@@ -216,7 +216,7 @@
     "name": "checkout pigweed.change data.changes.x:1234",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@3@@@",
-      "@@@STEP_SUMMARY_TEXT@Change(number=1234, remote='https://x.googlesource.com/b/c/d', ref='2d72510e447ab60a9728aeea2362d8be2cbd7789', rebase=False, project='pigweed', branch='main', gerrit_name='x', submitted=True, patchset=None, base=None, base_type=None, is_merge=False, commit_message='')@@@"
+      "@@@STEP_SUMMARY_TEXT@Change(number=1234, remote='https://x.googlesource.com/baz', ref='2d72510e447ab60a9728aeea2362d8be2cbd7789', rebase=False, project='pigweed', branch='main', gerrit_name='x', submitted=True, patchset=None, base=None, base_type=None, is_merge=False, commit_message='')@@@"
     ]
   },
   {
@@ -960,7 +960,7 @@
       "--init",
       "--jobs",
       "4",
-      "[START_DIR]/co/b/c/d"
+      "[START_DIR]/co/baz"
     ],
     "cwd": "[START_DIR]/co",
     "luci_context": {
@@ -986,9 +986,9 @@
     "name": "checkout pigweed.apply x:1234",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_SUMMARY_TEXT@b/c/d@@@",
+      "@@@STEP_SUMMARY_TEXT@baz@@@",
       "@@@STEP_LINK@gerrit@https://x-review.googlesource.com/c/1234@@@",
-      "@@@STEP_LINK@gitiles@https://x.googlesource.com/b/c/d/+/2d72510e447ab60a9728aeea2362d8be2cbd7789@@@"
+      "@@@STEP_LINK@gitiles@https://x.googlesource.com/baz/+/2d72510e447ab60a9728aeea2362d8be2cbd7789@@@"
     ]
   },
   {
@@ -1004,11 +1004,11 @@
       "fetch",
       "--jobs",
       "4",
-      "https://x.googlesource.com/b/c/d",
+      "https://x.googlesource.com/baz",
       "2d72510e447ab60a9728aeea2362d8be2cbd7789",
       "--no-recurse-submodules"
     ],
-    "cwd": "[START_DIR]/co/b/c/d",
+    "cwd": "[START_DIR]/co/baz",
     "infra_step": true,
     "luci_context": {
       "realm": {
@@ -1037,7 +1037,7 @@
       "working",
       "FETCH_HEAD"
     ],
-    "cwd": "[START_DIR]/co/b/c/d",
+    "cwd": "[START_DIR]/co/baz",
     "infra_step": true,
     "luci_context": {
       "realm": {
@@ -1062,10 +1062,10 @@
       "git",
       "remote",
       "add",
-      "https___x_googlesource_com_b_c_d",
-      "https://x.googlesource.com/b/c/d"
+      "https___x_googlesource_com_baz",
+      "https://x.googlesource.com/baz"
     ],
-    "cwd": "[START_DIR]/co/b/c/d",
+    "cwd": "[START_DIR]/co/baz",
     "infra_step": true,
     "luci_context": {
       "realm": {
@@ -1098,10 +1098,10 @@
       "fetch",
       "--jobs",
       "4",
-      "https___x_googlesource_com_b_c_d",
+      "https___x_googlesource_com_baz",
       "refs/heads/main"
     ],
-    "cwd": "[START_DIR]/co/b/c/d",
+    "cwd": "[START_DIR]/co/baz",
     "infra_step": true,
     "luci_context": {
       "realm": {
@@ -1125,9 +1125,9 @@
     "cmd": [
       "git",
       "branch",
-      "--set-upstream-to=https___x_googlesource_com_b_c_d/main"
+      "--set-upstream-to=https___x_googlesource_com_baz/main"
     ],
-    "cwd": "[START_DIR]/co/b/c/d",
+    "cwd": "[START_DIR]/co/baz",
     "infra_step": true,
     "luci_context": {
       "realm": {
@@ -1153,7 +1153,7 @@
       "rev-parse",
       "HEAD"
     ],
-    "cwd": "[START_DIR]/co/b/c/d",
+    "cwd": "[START_DIR]/co/baz",
     "luci_context": {
       "realm": {
         "name": "project:ci"
@@ -1185,7 +1185,7 @@
       "checkout",
       "--detach"
     ],
-    "cwd": "[START_DIR]/co/b/c/d",
+    "cwd": "[START_DIR]/co/baz",
     "luci_context": {
       "realm": {
         "name": "project:ci"
@@ -1214,7 +1214,7 @@
       "--jobs",
       "4"
     ],
-    "cwd": "[START_DIR]/co/b/c/d",
+    "cwd": "[START_DIR]/co/baz",
     "luci_context": {
       "realm": {
         "name": "project:ci"
@@ -1239,7 +1239,7 @@
       "checkout",
       "-"
     ],
-    "cwd": "[START_DIR]/co/b/c/d",
+    "cwd": "[START_DIR]/co/baz",
     "luci_context": {
       "realm": {
         "name": "project:ci"
@@ -1263,7 +1263,7 @@
     "name": "checkout pigweed.status",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_SUMMARY_TEXT@applied [Change(number=1234, remote='https://x.googlesource.com/b/c/d', ref='2d72510e447ab60a9728aeea2362d8be2cbd7789', rebase=False, project='pigweed', branch='main', gerrit_name='x', submitted=True, patchset=None, base='HEAD_HEAD_HEAD_HEAD_HEAD_HEAD_HEAD_HEAD_', base_type='submitted_commit_hash', is_merge=False, commit_message='')]\nnot applied []@@@"
+      "@@@STEP_SUMMARY_TEXT@applied [Change(number=1234, remote='https://x.googlesource.com/baz', ref='2d72510e447ab60a9728aeea2362d8be2cbd7789', rebase=False, project='pigweed', branch='main', gerrit_name='x', submitted=True, patchset=None, base='HEAD_HEAD_HEAD_HEAD_HEAD_HEAD_HEAD_HEAD_', base_type='submitted_commit_hash', is_merge=False, commit_message='')]\nnot applied []@@@"
     ]
   },
   {
@@ -1274,7 +1274,7 @@
       "--json-output",
       "/path/to/tmp/json",
       "copy",
-      "[{\"applied\": true, \"base\": \"HEAD_HEAD_HEAD_HEAD_HEAD_HEAD_HEAD_HEAD_\", \"base_type\": \"submitted_commit_hash\", \"branch\": \"main\", \"commit_message\": \"\", \"gerrit_name\": \"x\", \"is_merge\": false, \"number\": 1234, \"patchset\": null, \"project\": \"pigweed\", \"rebase\": false, \"ref\": \"2d72510e447ab60a9728aeea2362d8be2cbd7789\", \"remote\": \"https://x.googlesource.com/b/c/d\", \"submitted\": true}]",
+      "[{\"applied\": true, \"base\": \"HEAD_HEAD_HEAD_HEAD_HEAD_HEAD_HEAD_HEAD_\", \"base_type\": \"submitted_commit_hash\", \"branch\": \"main\", \"commit_message\": \"\", \"gerrit_name\": \"x\", \"is_merge\": false, \"number\": 1234, \"patchset\": null, \"project\": \"pigweed\", \"rebase\": false, \"ref\": \"2d72510e447ab60a9728aeea2362d8be2cbd7789\", \"remote\": \"https://x.googlesource.com/baz\", \"submitted\": true}]",
       "[CLEANUP]/tmp_tmp_1"
     ],
     "cwd": "[START_DIR]/co",
@@ -1294,7 +1294,7 @@
     "name": "checkout pigweed.write changes.json",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_LOG_LINE@tmp_tmp_1@[{\"applied\": true, \"base\": \"HEAD_HEAD_HEAD_HEAD_HEAD_HEAD_HEAD_HEAD_\", \"base_type\": \"submitted_commit_hash\", \"branch\": \"main\", \"commit_message\": \"\", \"gerrit_name\": \"x\", \"is_merge\": false, \"number\": 1234, \"patchset\": null, \"project\": \"pigweed\", \"rebase\": false, \"ref\": \"2d72510e447ab60a9728aeea2362d8be2cbd7789\", \"remote\": \"https://x.googlesource.com/b/c/d\", \"submitted\": true}]@@@",
+      "@@@STEP_LOG_LINE@tmp_tmp_1@[{\"applied\": true, \"base\": \"HEAD_HEAD_HEAD_HEAD_HEAD_HEAD_HEAD_HEAD_\", \"base_type\": \"submitted_commit_hash\", \"branch\": \"main\", \"commit_message\": \"\", \"gerrit_name\": \"x\", \"is_merge\": false, \"number\": 1234, \"patchset\": null, \"project\": \"pigweed\", \"rebase\": false, \"ref\": \"2d72510e447ab60a9728aeea2362d8be2cbd7789\", \"remote\": \"https://x.googlesource.com/baz\", \"submitted\": true}]@@@",
       "@@@STEP_LOG_END@tmp_tmp_1@@@"
     ]
   },
diff --git a/recipe_modules/checkout/tests/submodule.expected/submodule-not-initialized.json b/recipe_modules/checkout/tests/submodule.expected/submodule-not-initialized.json
new file mode 100644
index 0000000..8ce1d12
--- /dev/null
+++ b/recipe_modules/checkout/tests/submodule.expected/submodule-not-initialized.json
@@ -0,0 +1,973 @@
+[
+  {
+    "cmd": [],
+    "name": "checkout pigweed",
+    "~followup_annotations": [
+      "@@@STEP_EXCEPTION@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.options",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_SUMMARY_TEXT@remote: \"https://pigweed.googlesource.com/pigweed/pigweed.git\"\nbranch: \"main\"\nmatch_branch: true\nuse_trigger: true\n@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.options with defaults",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_SUMMARY_TEXT@remote: \"https://pigweed.googlesource.com/pigweed/pigweed.git\"\nbranch: \"main\"\nmanifest_file: \"default.xml\"\nrepo_init_timeout_sec: 20\nrepo_sync_timeout_sec: 120\nnumber_of_attempts: 3\nmatch_branch: true\nsubmodule_timeout_sec: 600\nuse_trigger: true\n@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.change data",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.change data.process gitiles commit",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.change data.process gitiles commit.ensure infra/tools/luci/gerrit/${platform}",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@3@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.change data.process gitiles commit.ensure infra/tools/luci/gerrit/${platform}.get packages",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@4@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "RECIPE_MODULE[fuchsia::gerrit]/resources/cipd.ensure",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.change data.process gitiles commit.ensure infra/tools/luci/gerrit/${platform}.get packages.read ensure file",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@5@@@",
+      "@@@STEP_LOG_LINE@cipd.ensure@infra/tools/luci/gerrit/${platform} version:pinned-version@@@",
+      "@@@STEP_LOG_END@cipd.ensure@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.change data.process gitiles commit.ensure infra/tools/luci/gerrit/${platform}.install infra/tools/luci/gerrit",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@4@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0o777",
+      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.change data.process gitiles commit.ensure infra/tools/luci/gerrit/${platform}.install infra/tools/luci/gerrit.ensure package directory",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@5@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07",
+      "-ensure-file",
+      "infra/tools/luci/gerrit/${platform} version:pinned-version",
+      "-max-threads",
+      "0",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.change data.process gitiles commit.ensure infra/tools/luci/gerrit/${platform}.install infra/tools/luci/gerrit.ensure_installed",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@5@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-version:pinned-v\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"infra/tools/luci/gerrit/resolved-platform\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07/gerrit",
+      "change-query",
+      "-host",
+      "https://x-review.googlesource.com",
+      "-input",
+      "{\"params\": {\"q\": \"commit:2d72510e447ab60a9728aeea2362d8be2cbd7789\"}}",
+      "-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.change data.process gitiles commit.number",
+    "timeout": 30,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@3@@@",
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"_number\": \"1234\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"branch\": \"main\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"project\": \"pigweed\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@json.input@{@@@",
+      "@@@STEP_LOG_LINE@json.input@  \"params\": {@@@",
+      "@@@STEP_LOG_LINE@json.input@    \"q\": \"commit:2d72510e447ab60a9728aeea2362d8be2cbd7789\"@@@",
+      "@@@STEP_LOG_LINE@json.input@  }@@@",
+      "@@@STEP_LOG_LINE@json.input@}@@@",
+      "@@@STEP_LOG_END@json.input@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.change data.changes",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.change data.changes.x:1234",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@3@@@",
+      "@@@STEP_SUMMARY_TEXT@Change(number=1234, remote='https://x.googlesource.com/b/c/d', ref='2d72510e447ab60a9728aeea2362d8be2cbd7789', rebase=False, project='pigweed', branch='main', gerrit_name='x', submitted=True, patchset=None, base=None, base_type=None, is_merge=False, commit_message='')@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.no non-standard branch names",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.cache",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_SUMMARY_TEXT@miss@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0o777",
+      "[CACHE]/git"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.cache.ensure git cache dir",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "",
+      "[CACHE]/git/.GUARD_FILE"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.cache.write git cache guard file",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0o777",
+      "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.cache.makedirs",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "init"
+    ],
+    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.cache.git init",
+    "timeout": 300.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "config",
+      "remote.origin.url",
+      "https://pigweed.googlesource.com/pigweed/pigweed"
+    ],
+    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.cache.remote set-url",
+    "timeout": 300.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "config",
+      "fetch.uriprotocols",
+      "https"
+    ],
+    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.cache.set fetch.uriprotocols",
+    "timeout": 300.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.cache.timeout 10s",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "fetch",
+      "--prune",
+      "--tags",
+      "--jobs",
+      "4",
+      "origin",
+      "--no-recurse-submodules"
+    ],
+    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.cache.git fetch",
+    "timeout": 1200.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "merge",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.cache.git merge",
+    "timeout": 600.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.cache.timeout 10s (2)",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--recursive",
+      "--force",
+      "--jobs",
+      "4"
+    ],
+    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.cache.git submodule update",
+    "timeout": 600,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "remove",
+      "[CACHE]/git/.GUARD_FILE"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.cache.remove git cache guard file",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copytree",
+      "--symlinks",
+      "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
+      "[START_DIR]/co"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.copy from cache",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.git checkout",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.git checkout.timeout 10s",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0o777",
+      "[START_DIR]/co"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "deadline": {
+        "grace_period": 30.0,
+        "soft_deadline": 1337000019.0
+      },
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.git checkout.makedirs",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "init"
+    ],
+    "cwd": "[START_DIR]/co",
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.git checkout.git init",
+    "timeout": 300.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "remote",
+      "add",
+      "origin",
+      "https://pigweed.googlesource.com/pigweed/pigweed"
+    ],
+    "cwd": "[START_DIR]/co",
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.git checkout.git remote",
+    "timeout": 600.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "config",
+      "core.longpaths",
+      "true"
+    ],
+    "cwd": "[START_DIR]/co",
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.git checkout.set core.longpaths",
+    "timeout": 300.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "config",
+      "fetch.uriprotocols",
+      "https"
+    ],
+    "cwd": "[START_DIR]/co",
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.git checkout.set fetch.uriprotocols",
+    "timeout": 300.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "fetch",
+      "--tags",
+      "--jobs",
+      "4",
+      "origin",
+      "main"
+    ],
+    "cwd": "[START_DIR]/co",
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.git checkout.git fetch",
+    "timeout": 1200.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/co",
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.git checkout.git checkout",
+    "timeout": 600.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/co",
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.git checkout.git rev-parse",
+    "timeout": 300.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/co",
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.git checkout.git clean",
+    "timeout": 600.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/co",
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.git rev-parse",
+    "timeout": 300.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python3",
+      "RECIPE_MODULE[pigweed::checkout]/resources/submodule_status.py",
+      "[START_DIR]/co",
+      "/path/to/tmp/json",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/co",
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.submodule status",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"b/c/d\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"branch\": \"main\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"conflict\": false,@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"describe\": \"\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"fetchRecurseSubmodules\": null,@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"hash\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ignore\": null,@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"initialized\": false,@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"modified\": false,@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"name\": \"b/c/d\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"path\": \"b/c/d\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"remote\": \"https://x.googlesource.com/b/c/d\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"shallow\": false,@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"update\": null,@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"url\": \"https://x.googlesource.com/b/c/d\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  },@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"bar\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"branch\": \"main\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"conflict\": false,@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"describe\": \"\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"fetchRecurseSubmodules\": null,@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"hash\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ignore\": null,@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"initialized\": true,@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"modified\": true,@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"name\": \"bar\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"path\": \"bar\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"remote\": \"https://x.googlesource.com/bar\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"shallow\": false,@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"update\": null,@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"url\": \"https://x.googlesource.com/bar\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  },@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"baz\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"branch\": \"main\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"conflict\": false,@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"describe\": \"\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"fetchRecurseSubmodules\": null,@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"hash\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ignore\": null,@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"initialized\": true,@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"modified\": false,@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"name\": \"baz\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"path\": \"baz\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"remote\": \"https://x.googlesource.com/baz.git\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"shallow\": false,@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"update\": null,@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"url\": \"https://x.googlesource.com/baz.git\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  },@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"foo\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"branch\": \"main\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"conflict\": false,@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"describe\": \"\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"fetchRecurseSubmodules\": null,@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"hash\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"ignore\": null,@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"initialized\": true,@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"modified\": false,@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"name\": \"foo\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"path\": \"foo\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"remote\": \"https://x.googlesource.com/foo\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"shallow\": false,@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"update\": null,@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"url\": \"https://x.googlesource.com/foo\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.no changes were applied",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@SET_BUILD_PROPERTY@changes@[\"x:1234\"]@@@",
+      "@@@STEP_FAILURE@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.no changes were applied.failed to apply x:1234",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LINK@gerrit@https://x-review.googlesource.com/c/1234@@@",
+      "@@@STEP_LINK@gitiles@https://x.googlesource.com/b/c/d/+/2d72510e447ab60a9728aeea2362d8be2cbd7789@@@",
+      "@@@STEP_WARNINGS@@@"
+    ]
+  },
+  {
+    "failure": {
+      "humanReason": "could not find triggering changes in checkout"
+    },
+    "name": "$result"
+  }
+]
\ No newline at end of file
diff --git a/recipe_modules/checkout/tests/submodule.py b/recipe_modules/checkout/tests/submodule.py
index c8dfef7..62f398d 100644
--- a/recipe_modules/checkout/tests/submodule.py
+++ b/recipe_modules/checkout/tests/submodule.py
@@ -65,6 +65,13 @@
     yield (
         api.test('submodule-ci')
         + properties()
+        + api.checkout.ci_test_data(git_repo='https://x.googlesource.com/baz')
+        + submodule_data
+    )
+
+    yield (
+        api.test('submodule-not-initialized', status='INFRA_FAILURE')
+        + properties()
         + api.checkout.ci_test_data(git_repo='https://x.googlesource.com/b/c/d')
         + submodule_data
     )