Reland "roller: Cancel any MODULE.bazel.lock-only rolls"

This is a reland of commit f7abc5c6ed02e91c069bafbe3feb711301ec8eda

Original change's description:
> roller: Cancel any MODULE.bazel.lock-only rolls
>
> Change-Id: I35c3abd7f2180574998af9271cbcc527967eb626
> Reviewed-on: https://pigweed-review.googlesource.com/c/infra/recipes/+/411852

Change-Id: If55064b32ed07d900a2a78b83606b602e05db0c6
Reviewed-on: https://pigweed-review.googlesource.com/c/infra/recipes/+/412277
diff --git a/recipes/roller.expected/noop.json b/recipes/roller.expected/noop.json
index 94cbad6..95c91bd 100644
--- a/recipes/roller.expected/noop.json
+++ b/recipes/roller.expected/noop.json
@@ -3817,7 +3817,8 @@
     "~followup_annotations": [
       "@@@SET_BUILD_PROPERTY@_roll_count@0@@@",
       "@@@SET_BUILD_PROPERTY@_roll_names@[]@@@",
-      "@@@SET_BUILD_PROPERTY@ready_to_upload@true@@@"
+      "@@@SET_BUILD_PROPERTY@ready_to_upload@true@@@",
+      "@@@STEP_FAILURE@@@"
     ]
   },
   {
diff --git a/recipes/roller.expected/only_lock_rolls.json b/recipes/roller.expected/only_lock_rolls.json
new file mode 100644
index 0000000..53c5ad1
--- /dev/null
+++ b/recipes/roller.expected/only_lock_rolls.json
@@ -0,0 +1,1154 @@
+[
+  {
+    "cmd": [],
+    "name": "abandon old changes"
+  },
+  {
+    "cmd": [],
+    "name": "abandon old changes.pigweed",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "abandon old changes.pigweed.ensure infra/tools/luci/gerrit/${platform}",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "abandon old changes.pigweed.ensure infra/tools/luci/gerrit/${platform}.get packages",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@3@@@"
+    ]
+  },
+  {
+    "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,
+    "name": "abandon old changes.pigweed.ensure infra/tools/luci/gerrit/${platform}.get packages.read ensure file",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@4@@@",
+      "@@@STEP_LOG_LINE@cipd.ensure@infra/tools/luci/gerrit/${platform} version:pinned-version@@@",
+      "@@@STEP_LOG_END@cipd.ensure@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "abandon old changes.pigweed.ensure infra/tools/luci/gerrit/${platform}.install infra/tools/luci/gerrit",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@3@@@"
+    ]
+  },
+  {
+    "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,
+    "name": "abandon old changes.pigweed.ensure infra/tools/luci/gerrit/${platform}.install infra/tools/luci/gerrit.ensure package directory",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@4@@@"
+    ]
+  },
+  {
+    "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,
+    "name": "abandon old changes.pigweed.ensure infra/tools/luci/gerrit/${platform}.install infra/tools/luci/gerrit.ensure_installed",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@4@@@",
+      "@@@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://pigweed-review.googlesource.com",
+      "-input",
+      "{\"params\": {\"q\": \"is:open owner:None age:30d \"}}",
+      "-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "name": "abandon old changes.pigweed.get old changes",
+    "timeout": 30,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"_number\": 1001@@@",
+      "@@@STEP_LOG_LINE@json.output@  },@@@",
+      "@@@STEP_LOG_LINE@json.output@  {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"_number\": 1002@@@",
+      "@@@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\": \"is:open owner:None age:30d \"@@@",
+      "@@@STEP_LOG_LINE@json.input@  }@@@",
+      "@@@STEP_LOG_LINE@json.input@}@@@",
+      "@@@STEP_LOG_END@json.input@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07/gerrit",
+      "change-abandon",
+      "-host",
+      "https://pigweed-review.googlesource.com",
+      "-input",
+      "{\"change_id\": \"1001\", \"input\": {\"message\": \"Abandoning orphaned roll change.\"}}",
+      "-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "name": "abandon old changes.pigweed.abandon 1001",
+    "timeout": 600,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_END@json.output (invalid)@@@",
+      "@@@STEP_LOG_LINE@json.output (exception)@Expecting value: line 1 column 1 (char 0)@@@",
+      "@@@STEP_LOG_END@json.output (exception)@@@",
+      "@@@STEP_LOG_LINE@json.input@{@@@",
+      "@@@STEP_LOG_LINE@json.input@  \"change_id\": \"1001\",@@@",
+      "@@@STEP_LOG_LINE@json.input@  \"input\": {@@@",
+      "@@@STEP_LOG_LINE@json.input@    \"message\": \"Abandoning orphaned roll change.\"@@@",
+      "@@@STEP_LOG_LINE@json.input@  }@@@",
+      "@@@STEP_LOG_LINE@json.input@}@@@",
+      "@@@STEP_LOG_END@json.input@@@",
+      "@@@STEP_LINK@gerrit link@https://pigweed-review.googlesource.com/q/1001@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07/gerrit",
+      "change-abandon",
+      "-host",
+      "https://pigweed-review.googlesource.com",
+      "-input",
+      "{\"change_id\": \"1002\", \"input\": {\"message\": \"Abandoning orphaned roll change.\"}}",
+      "-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "name": "abandon old changes.pigweed.abandon 1002",
+    "timeout": 600,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_END@json.output (invalid)@@@",
+      "@@@STEP_LOG_LINE@json.output (exception)@Expecting value: line 1 column 1 (char 0)@@@",
+      "@@@STEP_LOG_END@json.output (exception)@@@",
+      "@@@STEP_LOG_LINE@json.input@{@@@",
+      "@@@STEP_LOG_LINE@json.input@  \"change_id\": \"1002\",@@@",
+      "@@@STEP_LOG_LINE@json.input@  \"input\": {@@@",
+      "@@@STEP_LOG_LINE@json.input@    \"message\": \"Abandoning orphaned roll change.\"@@@",
+      "@@@STEP_LOG_LINE@json.input@  }@@@",
+      "@@@STEP_LOG_LINE@json.input@}@@@",
+      "@@@STEP_LOG_END@json.input@@@",
+      "@@@STEP_LINK@gerrit link@https://pigweed-review.googlesource.com/q/1002@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "abandon old changes.pigweed.success",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "not ready",
+    "~followup_annotations": [
+      "@@@SET_BUILD_PROPERTY@ready_to_upload@false@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed"
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.options",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_SUMMARY_TEXT@remote: \"https://pigweed.googlesource.com/pigweed/pigweed\"\nbranch: \"main\"\ninitialize_submodules: true\nmatch_branch: 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\"\nbranch: \"main\"\nmanifest_file: \"default.xml\"\ninitialize_submodules: true\nrepo_init_timeout_sec: 120\nrepo_sync_timeout_sec: 900\nnumber_of_attempts: 3\nmatch_branch: true\nsubmodule_timeout_sec: 600\nmax_attempts: 2\n@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.not matching 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,
+    "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,
+    "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,
+    "name": "checkout pigweed.cache.makedirs",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.cache.miss",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@SET_BUILD_PROPERTY@checkout https://pigweed.googlesource.com/pigweed/pigweed@\"cache miss\"@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "init"
+    ],
+    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "4",
+      "GIT_CONFIG_KEY_0": "fetch.unpackLimit",
+      "GIT_CONFIG_KEY_1": "gc.auto",
+      "GIT_CONFIG_KEY_2": "gc.autoDetach",
+      "GIT_CONFIG_KEY_3": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "1",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0",
+      "GIT_CONFIG_VALUE_3": "0"
+    },
+    "infra_step": true,
+    "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",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "4",
+      "GIT_CONFIG_KEY_0": "fetch.unpackLimit",
+      "GIT_CONFIG_KEY_1": "gc.auto",
+      "GIT_CONFIG_KEY_2": "gc.autoDetach",
+      "GIT_CONFIG_KEY_3": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "1",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0",
+      "GIT_CONFIG_VALUE_3": "0"
+    },
+    "infra_step": true,
+    "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",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "4",
+      "GIT_CONFIG_KEY_0": "fetch.unpackLimit",
+      "GIT_CONFIG_KEY_1": "gc.auto",
+      "GIT_CONFIG_KEY_2": "gc.autoDetach",
+      "GIT_CONFIG_KEY_3": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "1",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0",
+      "GIT_CONFIG_VALUE_3": "0"
+    },
+    "infra_step": true,
+    "name": "checkout pigweed.cache.enable packfiles",
+    "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",
+      "--recurse-submodules"
+    ],
+    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "4",
+      "GIT_CONFIG_KEY_0": "fetch.unpackLimit",
+      "GIT_CONFIG_KEY_1": "gc.auto",
+      "GIT_CONFIG_KEY_2": "gc.autoDetach",
+      "GIT_CONFIG_KEY_3": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "1",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0",
+      "GIT_CONFIG_VALUE_3": "0"
+    },
+    "infra_step": true,
+    "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",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "4",
+      "GIT_CONFIG_KEY_0": "fetch.unpackLimit",
+      "GIT_CONFIG_KEY_1": "gc.auto",
+      "GIT_CONFIG_KEY_2": "gc.autoDetach",
+      "GIT_CONFIG_KEY_3": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "1",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0",
+      "GIT_CONFIG_VALUE_3": "0"
+    },
+    "infra_step": true,
+    "name": "checkout pigweed.cache.git merge",
+    "timeout": 600.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync",
+      "--recursive"
+    ],
+    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "4",
+      "GIT_CONFIG_KEY_0": "fetch.unpackLimit",
+      "GIT_CONFIG_KEY_1": "gc.auto",
+      "GIT_CONFIG_KEY_2": "gc.autoDetach",
+      "GIT_CONFIG_KEY_3": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "1",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0",
+      "GIT_CONFIG_VALUE_3": "0"
+    },
+    "infra_step": true,
+    "name": "checkout pigweed.cache.git submodule sync",
+    "timeout": 1200.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",
+      "--init",
+      "--recursive",
+      "--force",
+      "--jobs",
+      "4"
+    ],
+    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "4",
+      "GIT_CONFIG_KEY_0": "fetch.unpackLimit",
+      "GIT_CONFIG_KEY_1": "gc.auto",
+      "GIT_CONFIG_KEY_2": "gc.autoDetach",
+      "GIT_CONFIG_KEY_3": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "1",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0",
+      "GIT_CONFIG_VALUE_3": "0"
+    },
+    "infra_step": true,
+    "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,
+    "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,
+    "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
+      }
+    },
+    "name": "checkout pigweed.git checkout.makedirs",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "init"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "4",
+      "GIT_CONFIG_KEY_0": "fetch.unpackLimit",
+      "GIT_CONFIG_KEY_1": "gc.auto",
+      "GIT_CONFIG_KEY_2": "gc.autoDetach",
+      "GIT_CONFIG_KEY_3": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "1",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0",
+      "GIT_CONFIG_VALUE_3": "0"
+    },
+    "infra_step": true,
+    "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",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "4",
+      "GIT_CONFIG_KEY_0": "fetch.unpackLimit",
+      "GIT_CONFIG_KEY_1": "gc.auto",
+      "GIT_CONFIG_KEY_2": "gc.autoDetach",
+      "GIT_CONFIG_KEY_3": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "1",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0",
+      "GIT_CONFIG_VALUE_3": "0"
+    },
+    "infra_step": true,
+    "name": "checkout pigweed.git checkout.git remote",
+    "timeout": 1200.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "config",
+      "core.longpaths",
+      "true"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "4",
+      "GIT_CONFIG_KEY_0": "fetch.unpackLimit",
+      "GIT_CONFIG_KEY_1": "gc.auto",
+      "GIT_CONFIG_KEY_2": "gc.autoDetach",
+      "GIT_CONFIG_KEY_3": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "1",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0",
+      "GIT_CONFIG_VALUE_3": "0"
+    },
+    "infra_step": true,
+    "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",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "4",
+      "GIT_CONFIG_KEY_0": "fetch.unpackLimit",
+      "GIT_CONFIG_KEY_1": "gc.auto",
+      "GIT_CONFIG_KEY_2": "gc.autoDetach",
+      "GIT_CONFIG_KEY_3": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "1",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0",
+      "GIT_CONFIG_VALUE_3": "0"
+    },
+    "infra_step": true,
+    "name": "checkout pigweed.git checkout.enable packfiles",
+    "timeout": 300.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "fetch",
+      "--tags",
+      "--jobs",
+      "4",
+      "origin",
+      "main",
+      "--recurse-submodules"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "4",
+      "GIT_CONFIG_KEY_0": "fetch.unpackLimit",
+      "GIT_CONFIG_KEY_1": "gc.auto",
+      "GIT_CONFIG_KEY_2": "gc.autoDetach",
+      "GIT_CONFIG_KEY_3": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "1",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0",
+      "GIT_CONFIG_VALUE_3": "0"
+    },
+    "infra_step": true,
+    "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",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "4",
+      "GIT_CONFIG_KEY_0": "fetch.unpackLimit",
+      "GIT_CONFIG_KEY_1": "gc.auto",
+      "GIT_CONFIG_KEY_2": "gc.autoDetach",
+      "GIT_CONFIG_KEY_3": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "1",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0",
+      "GIT_CONFIG_VALUE_3": "0"
+    },
+    "infra_step": true,
+    "name": "checkout pigweed.git checkout.git checkout",
+    "timeout": 1200.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "4",
+      "GIT_CONFIG_KEY_0": "fetch.unpackLimit",
+      "GIT_CONFIG_KEY_1": "gc.auto",
+      "GIT_CONFIG_KEY_2": "gc.autoDetach",
+      "GIT_CONFIG_KEY_3": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "1",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0",
+      "GIT_CONFIG_VALUE_3": "0"
+    },
+    "infra_step": true,
+    "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",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "4",
+      "GIT_CONFIG_KEY_0": "fetch.unpackLimit",
+      "GIT_CONFIG_KEY_1": "gc.auto",
+      "GIT_CONFIG_KEY_2": "gc.autoDetach",
+      "GIT_CONFIG_KEY_3": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "1",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0",
+      "GIT_CONFIG_VALUE_3": "0"
+    },
+    "infra_step": true,
+    "name": "checkout pigweed.git checkout.git clean",
+    "timeout": 1200.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.git checkout.submodule",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.git checkout.submodule.submodule",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@3@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "4",
+      "GIT_CONFIG_KEY_0": "fetch.unpackLimit",
+      "GIT_CONFIG_KEY_1": "gc.auto",
+      "GIT_CONFIG_KEY_2": "gc.autoDetach",
+      "GIT_CONFIG_KEY_3": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "1",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0",
+      "GIT_CONFIG_VALUE_3": "0"
+    },
+    "infra_step": true,
+    "name": "checkout pigweed.git checkout.submodule.submodule.git submodule sync",
+    "timeout": 1200.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@4@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive",
+      "--force",
+      "--jobs",
+      "4"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "4",
+      "GIT_CONFIG_KEY_0": "fetch.unpackLimit",
+      "GIT_CONFIG_KEY_1": "gc.auto",
+      "GIT_CONFIG_KEY_2": "gc.autoDetach",
+      "GIT_CONFIG_KEY_3": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "1",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0",
+      "GIT_CONFIG_VALUE_3": "0"
+    },
+    "infra_step": true,
+    "name": "checkout pigweed.git checkout.submodule.submodule.git submodule update",
+    "timeout": 600,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@4@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.git log",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "log",
+      "--oneline",
+      "-n",
+      "10"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "4",
+      "GIT_CONFIG_KEY_0": "fetch.unpackLimit",
+      "GIT_CONFIG_KEY_1": "gc.auto",
+      "GIT_CONFIG_KEY_2": "gc.autoDetach",
+      "GIT_CONFIG_KEY_3": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "1",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0",
+      "GIT_CONFIG_VALUE_3": "0"
+    },
+    "name": "checkout pigweed.git log.[START_DIR]/co",
+    "timeout": 1200.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-f",
+      "-d"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "4",
+      "GIT_CONFIG_KEY_0": "fetch.unpackLimit",
+      "GIT_CONFIG_KEY_1": "gc.auto",
+      "GIT_CONFIG_KEY_2": "gc.autoDetach",
+      "GIT_CONFIG_KEY_3": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "1",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0",
+      "GIT_CONFIG_VALUE_3": "0"
+    },
+    "name": "checkout pigweed.git clean",
+    "timeout": 1200.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "status"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "4",
+      "GIT_CONFIG_KEY_0": "fetch.unpackLimit",
+      "GIT_CONFIG_KEY_1": "gc.auto",
+      "GIT_CONFIG_KEY_2": "gc.autoDetach",
+      "GIT_CONFIG_KEY_3": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "1",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0",
+      "GIT_CONFIG_VALUE_3": "0"
+    },
+    "name": "checkout pigweed.git status",
+    "timeout": 1200.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0o777",
+      "[START_DIR]/snapshot"
+    ],
+    "infra_step": true,
+    "name": "checkout pigweed.mkdir",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "status",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "4",
+      "GIT_CONFIG_KEY_0": "fetch.unpackLimit",
+      "GIT_CONFIG_KEY_1": "gc.auto",
+      "GIT_CONFIG_KEY_2": "gc.autoDetach",
+      "GIT_CONFIG_KEY_3": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "1",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0",
+      "GIT_CONFIG_VALUE_3": "0"
+    },
+    "name": "checkout pigweed.submodule-status",
+    "timeout": 1200.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "submodule status filler text",
+      "[START_DIR]/snapshot/submodules.log"
+    ],
+    "infra_step": true,
+    "name": "checkout pigweed.write submodule snapshot",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@submodules.log@submodule status filler text@@@",
+      "@@@STEP_LOG_END@submodules.log@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "log",
+      "--oneline",
+      "-n",
+      "10"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "4",
+      "GIT_CONFIG_KEY_0": "fetch.unpackLimit",
+      "GIT_CONFIG_KEY_1": "gc.auto",
+      "GIT_CONFIG_KEY_2": "gc.autoDetach",
+      "GIT_CONFIG_KEY_3": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "1",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0",
+      "GIT_CONFIG_VALUE_3": "0"
+    },
+    "name": "checkout pigweed.log",
+    "timeout": 1200.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "",
+      "[START_DIR]/snapshot/git.log"
+    ],
+    "infra_step": true,
+    "name": "checkout pigweed.write git log",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_END@git.log@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "MODULE.bazel.lock"
+  },
+  {
+    "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[CLEANUP]/tmp_tmp_1",
+      "-ensure-file",
+      "fuchsia/third_party/bazelisk/${platform} latest",
+      "-max-threads",
+      "0",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "cwd": "[START_DIR]/co",
+    "infra_step": true,
+    "name": "MODULE.bazel.lock.ensure bazelisk",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@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-latest----------\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"fuchsia/third_party/bazelisk/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": [
+      "[CLEANUP]/tmp_tmp_1/bazelisk",
+      "mod",
+      "deps",
+      "--lockfile_mode=update"
+    ],
+    "cwd": "[START_DIR]/co",
+    "name": "MODULE.bazel.lock.bazelisk mod deps --lockfile_mode=update",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "diff",
+      "HEAD",
+      "--exit-code",
+      "[START_DIR]/co/MODULE.bazel.lock"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "4",
+      "GIT_CONFIG_KEY_0": "fetch.unpackLimit",
+      "GIT_CONFIG_KEY_1": "gc.auto",
+      "GIT_CONFIG_KEY_2": "gc.autoDetach",
+      "GIT_CONFIG_KEY_3": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "1",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0",
+      "GIT_CONFIG_VALUE_3": "0"
+    },
+    "name": "MODULE.bazel.lock.git diff",
+    "timeout": 300.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "no real rolls",
+    "~followup_annotations": [
+      "@@@SET_BUILD_PROPERTY@_roll_count@0@@@",
+      "@@@SET_BUILD_PROPERTY@_roll_names@[]@@@",
+      "@@@SET_BUILD_PROPERTY@ready_to_upload@true@@@",
+      "@@@STEP_FAILURE@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "summaryMarkdown": "only updating MODULE.bazel.lock files"
+  }
+]
\ No newline at end of file
diff --git a/recipes/roller.py b/recipes/roller.py
index e6eda71..530dedd 100644
--- a/recipes/roller.py
+++ b/recipes/roller.py
@@ -150,71 +150,72 @@
         env = api.environment.init(checkout, props.environment_options)
 
     rolls = []
-    attempts = 0
+    num_attempts = 0
+    lock_rolls = []
 
     with contextlib.ExitStack() as stack:
         if env:
             stack.enter_context(env())
 
         for submodule in props.submodules:
-            attempts += 1
+            num_attempts += 1
             rolls.extend(api.submodule_roll.update(checkout, submodule))
 
         for cipd_package in props.bazel_cipd_packages:
-            attempts += 1
+            num_attempts += 1
             rolls.extend(
                 api.bazel_roll.update_cipd_package(checkout, cipd_package),
             )
 
         for git_repo in props.bazel_git_repositories:
-            attempts += 1
+            num_attempts += 1
             rolls.extend(
                 api.bazel_roll.update_git_repository(checkout, git_repo),
             )
 
         for project in props.repo_tool_projects:
-            attempts += 1
+            num_attempts += 1
             rolls.extend(api.repo_roll.update_project(checkout, project))
 
         for cipd_package in props.cipd_packages:
-            attempts += 1
+            num_attempts += 1
             rolls.extend(api.cipd_roll.update_package(checkout, cipd_package))
 
         for cipd_variable in props.cipd_variables:
-            attempts += 1
+            num_attempts += 1
             rolls.extend(api.variable_roll.update_cipd(checkout, cipd_variable))
 
         for txt_entry in props.txt_entries:
-            attempts += 1
+            num_attempts += 1
             rolls.extend(api.txt_roll.update(checkout, txt_entry))
 
         for variable_entry in props.variable_entries:
-            attempts += 1
+            num_attempts += 1
             rolls.extend(api.variable_roll.update(checkout, variable_entry))
 
         for copy_entry in props.copy_entries:
-            attempts += 1
+            num_attempts += 1
             rolls.extend(api.copy_roll.update(checkout, copy_entry))
 
         for lock_entry in props.module_bazel_lock_entries:
-            attempts += 1
-            rolls.extend(
-                api.module_bazel_lock_roll.update(checkout, lock_entry),
-            )
+            num_attempts += 1
+            this_rolls = api.module_bazel_lock_roll.update(checkout, lock_entry)
+            lock_rolls.extend(this_rolls)
+            rolls.extend(this_rolls)
 
         for merge_entry in props.merge_entries:
-            attempts += 1
+            num_attempts += 1
             rolls.extend(
                 api.merge_roll.merge(checkout, merge_entry),
             )
 
         for run_script_entry in props.run_script_entries:
-            attempts += 1
+            num_attempts += 1
             rolls.extend(
                 api.run_script_roll.run(checkout, run_script_entry),
             )
 
-    if not attempts:
+    if not num_attempts:
         summary = 'roller is not configured to roll anything'
         pres = api.step.empty(summary).presentation
         pres.properties['ready_to_upload'] = True
@@ -226,7 +227,27 @@
 
     if not rolls:
         summary = 'nothing to roll'
-        pres = api.step.empty(summary).presentation
+        pres = api.step.empty(
+            summary,
+            status='FAILURE',
+            raise_on_failure=False,
+        ).presentation
+        pres.properties['_roll_count'] = 0
+        pres.properties['_roll_names'] = []
+        pres.properties['ready_to_upload'] = True
+
+        return result_pb.RawResult(
+            summary_markdown=summary,
+            status=common_pb.SUCCESS,
+        )
+
+    if len(rolls) == len(lock_rolls):
+        summary = 'only updating MODULE.bazel.lock files'
+        pres = api.step.empty(
+            'no real rolls',
+            status='FAILURE',
+            raise_on_failure=False,
+        ).presentation
         pres.properties['_roll_count'] = 0
         pres.properties['_roll_names'] = []
         pres.properties['ready_to_upload'] = True
@@ -721,3 +742,13 @@
         api.post_check(post_process.PropertyEquals, '_roll_count', 11),
         # api.post_process(post_process.DropExpectation),
     )
+
+    yield api.test(
+        'only_lock_rolls',
+        properties(
+            module_bazel_lock_entries=api.module_bazel_lock_roll.entry(),
+        ),
+        api.step_data('MODULE.bazel.lock.git diff', retcode=1),
+        api.post_check(post_process.MustRun, 'no real rolls'),
+        # api.post_process(post_process.DropExpectation),
+    )