Create recipe to call pub package autoroller script from framework

Design doc: http://goto.google.com/flutter-update-packages-autoroller

## Example of recipe creating a PR

LED build: https://luci-milo.appspot.com/raw/build/logs.chromium.org/flutter/led/fujino_google.com/1b37aff6d2c897f812a9385f3e54b0ab9280e1939a0303954ce762b04120abce/+/build.proto

Generated PR: https://github.com/flutter/flutter/pull/106693

## Example of recipe passing green because packages already at latest

LED build: https://luci-milo.appspot.com/raw/build/logs.chromium.org/flutter/led/fujino_google.com/e190770e89f00a29685cc4e5465a01a21b6a3fb0b4c4888f43b059e8dcba7c66/+/build.proto

Bug: https://github.com/flutter/flutter/issues/106371
Change-Id: Ifdfafd1724756410992fbf8d50204b797c8f6f62
Reviewed-on: https://flutter-review.googlesource.com/c/recipes/+/31620
Reviewed-by: Keyong Han <keyonghan@google.com>
Reviewed-by: Casey Hillers <chillers@google.com>
Commit-Queue: Christopher Fujino <fujino@google.com>
diff --git a/recipe_modules/flutter_deps/api.py b/recipe_modules/flutter_deps/api.py
index f28e5b6..756e40c 100644
--- a/recipe_modules/flutter_deps/api.py
+++ b/recipe_modules/flutter_deps/api.py
@@ -65,6 +65,7 @@
         'dart_sdk': self.dart_sdk,
         'dashing': self.dashing,
         'firebase': self.firebase,
+        'gh_cli': self.gh_cli,
         'go_sdk': self.go_sdk,
         'goldctl': self.goldctl,
         'gradle_cache': self.gradle_cache,
@@ -179,6 +180,18 @@
       else:
         env['CHROME_EXECUTABLE'] = chrome_path.join(binary_name)
 
+  def gh_cli(self, env, env_prefixes, version):
+    """Installs GitHub CLI."""
+    version = version or 'latest'
+    gh_path = self.m.path['cache'].join('gh-cli')
+    gh_file = self.m.cipd.EnsureFile()
+    gh_file.add_package('flutter_internal/tools/gh-cli/${platform}', version)
+    self.m.cipd.ensure(gh_path, gh_file)
+    self.m.step('check gh version', [gh_path.join('bin', 'gh'), '--version'])
+    paths = env_prefixes.get('PATH', [])
+    paths.append(gh_path.join('bin'))
+    env_prefixes['PATH'] = paths
+
   def go_sdk(self, env, env_prefixes, version):
     """Installs go sdk."""
     version = version or 'version:1.12.5'
diff --git a/recipe_modules/flutter_deps/examples/full.expected/basic.json b/recipe_modules/flutter_deps/examples/full.expected/basic.json
index 1235422..695aa43 100644
--- a/recipe_modules/flutter_deps/examples/full.expected/basic.json
+++ b/recipe_modules/flutter_deps/examples/full.expected/basic.json
@@ -704,6 +704,41 @@
   },
   {
     "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[CACHE]/gh-cli",
+      "-ensure-file",
+      "flutter_internal/tools/gh-cli/${platform} latest",
+      "-max-threads",
+      "0",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "name": "ensure_installed (7)",
+    "~followup_annotations": [
+      "@@@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\": \"flutter_internal/tools/gh-cli/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": [
+      "[CACHE]/gh-cli/bin/gh",
+      "--version"
+    ],
+    "name": "check gh version"
+  },
+  {
+    "cmd": [
       "git",
       "rev-parse",
       "HEAD"
diff --git a/recipe_modules/flutter_deps/examples/full.expected/flutter_engine.json b/recipe_modules/flutter_deps/examples/full.expected/flutter_engine.json
index b91b06d..2f62c72 100644
--- a/recipe_modules/flutter_deps/examples/full.expected/flutter_engine.json
+++ b/recipe_modules/flutter_deps/examples/full.expected/flutter_engine.json
@@ -790,6 +790,41 @@
   },
   {
     "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[CACHE]/gh-cli",
+      "-ensure-file",
+      "flutter_internal/tools/gh-cli/${platform} latest",
+      "-max-threads",
+      "0",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "name": "ensure_installed (7)",
+    "~followup_annotations": [
+      "@@@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\": \"flutter_internal/tools/gh-cli/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": [
+      "[CACHE]/gh-cli/bin/gh",
+      "--version"
+    ],
+    "name": "check gh version"
+  },
+  {
+    "cmd": [
       "git",
       "rev-parse",
       "HEAD"
diff --git a/recipe_modules/flutter_deps/examples/full.expected/goldTryjob.json b/recipe_modules/flutter_deps/examples/full.expected/goldTryjob.json
index 1235422..695aa43 100644
--- a/recipe_modules/flutter_deps/examples/full.expected/goldTryjob.json
+++ b/recipe_modules/flutter_deps/examples/full.expected/goldTryjob.json
@@ -704,6 +704,41 @@
   },
   {
     "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[CACHE]/gh-cli",
+      "-ensure-file",
+      "flutter_internal/tools/gh-cli/${platform} latest",
+      "-max-threads",
+      "0",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "name": "ensure_installed (7)",
+    "~followup_annotations": [
+      "@@@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\": \"flutter_internal/tools/gh-cli/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": [
+      "[CACHE]/gh-cli/bin/gh",
+      "--version"
+    ],
+    "name": "check gh version"
+  },
+  {
+    "cmd": [
       "git",
       "rev-parse",
       "HEAD"
diff --git a/recipe_modules/flutter_deps/examples/full.expected/linux.json b/recipe_modules/flutter_deps/examples/full.expected/linux.json
index 1235422..695aa43 100644
--- a/recipe_modules/flutter_deps/examples/full.expected/linux.json
+++ b/recipe_modules/flutter_deps/examples/full.expected/linux.json
@@ -704,6 +704,41 @@
   },
   {
     "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[CACHE]/gh-cli",
+      "-ensure-file",
+      "flutter_internal/tools/gh-cli/${platform} latest",
+      "-max-threads",
+      "0",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "name": "ensure_installed (7)",
+    "~followup_annotations": [
+      "@@@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\": \"flutter_internal/tools/gh-cli/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": [
+      "[CACHE]/gh-cli/bin/gh",
+      "--version"
+    ],
+    "name": "check gh version"
+  },
+  {
+    "cmd": [
       "git",
       "rev-parse",
       "HEAD"
diff --git a/recipe_modules/flutter_deps/examples/full.expected/local_engine_cas.json b/recipe_modules/flutter_deps/examples/full.expected/local_engine_cas.json
index cf66fdd..4cda25e 100644
--- a/recipe_modules/flutter_deps/examples/full.expected/local_engine_cas.json
+++ b/recipe_modules/flutter_deps/examples/full.expected/local_engine_cas.json
@@ -790,6 +790,41 @@
   },
   {
     "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[CACHE]/gh-cli",
+      "-ensure-file",
+      "flutter_internal/tools/gh-cli/${platform} latest",
+      "-max-threads",
+      "0",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "name": "ensure_installed (7)",
+    "~followup_annotations": [
+      "@@@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\": \"flutter_internal/tools/gh-cli/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": [
+      "[CACHE]/gh-cli/bin/gh",
+      "--version"
+    ],
+    "name": "check gh version"
+  },
+  {
+    "cmd": [
       "git",
       "rev-parse",
       "HEAD"
diff --git a/recipe_modules/flutter_deps/examples/full.expected/with-arm64ruby.json b/recipe_modules/flutter_deps/examples/full.expected/with-arm64ruby.json
index 10b9821..03e8d33 100644
--- a/recipe_modules/flutter_deps/examples/full.expected/with-arm64ruby.json
+++ b/recipe_modules/flutter_deps/examples/full.expected/with-arm64ruby.json
@@ -704,6 +704,41 @@
   },
   {
     "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[CACHE]/gh-cli",
+      "-ensure-file",
+      "flutter_internal/tools/gh-cli/${platform} latest",
+      "-max-threads",
+      "0",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "name": "ensure_installed (7)",
+    "~followup_annotations": [
+      "@@@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\": \"flutter_internal/tools/gh-cli/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": [
+      "[CACHE]/gh-cli/bin/gh",
+      "--version"
+    ],
+    "name": "check gh version"
+  },
+  {
+    "cmd": [
       "git",
       "rev-parse",
       "HEAD"
diff --git a/recipe_modules/flutter_deps/examples/full.expected/with-gems.json b/recipe_modules/flutter_deps/examples/full.expected/with-gems.json
index 7194b0b..21f7d4c 100644
--- a/recipe_modules/flutter_deps/examples/full.expected/with-gems.json
+++ b/recipe_modules/flutter_deps/examples/full.expected/with-gems.json
@@ -704,6 +704,41 @@
   },
   {
     "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[CACHE]/gh-cli",
+      "-ensure-file",
+      "flutter_internal/tools/gh-cli/${platform} latest",
+      "-max-threads",
+      "0",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "name": "ensure_installed (7)",
+    "~followup_annotations": [
+      "@@@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\": \"flutter_internal/tools/gh-cli/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": [
+      "[CACHE]/gh-cli/bin/gh",
+      "--version"
+    ],
+    "name": "check gh version"
+  },
+  {
+    "cmd": [
       "git",
       "rev-parse",
       "HEAD"
diff --git a/recipe_modules/flutter_deps/examples/full.py b/recipe_modules/flutter_deps/examples/full.py
index 8050472..23860bd 100644
--- a/recipe_modules/flutter_deps/examples/full.py
+++ b/recipe_modules/flutter_deps/examples/full.py
@@ -65,6 +65,7 @@
   api.flutter_deps.jazzy(env, env_prefixes, '')
   if api.platform.is_linux:
     api.flutter_deps.android_virtual_device(env, env_prefixes, '31')
+    api.flutter_deps.gh_cli(env, env_prefixes, 'latest')
 
   # Gems dependency requires to run from a flutter_environment.
   checkout_path = api.path['start_dir'].join('flutter\ sdk')
diff --git a/recipe_modules/kms/api.py b/recipe_modules/kms/api.py
index 7a643fb..83559c3 100644
--- a/recipe_modules/kms/api.py
+++ b/recipe_modules/kms/api.py
@@ -12,7 +12,7 @@
     """Decrypts the encrypted secret.
 
     Args:
-      input_file (str): encrypted file of the secret.
+      input_file (str): path on GCS to encrypted file of the secret relative to 'flutter_configs'.
       secret_path (Path): path of decrypted secret.
     """
     cloudkms_dir = self.m.path['start_dir'].join('cloudkms')
diff --git a/recipes/pub_autoroller/pub_autoroller.expected/roll.json b/recipes/pub_autoroller/pub_autoroller.expected/roll.json
new file mode 100644
index 0000000..bae96b6
--- /dev/null
+++ b/recipes/pub_autoroller/pub_autoroller.expected/roll.json
@@ -0,0 +1,263 @@
+[
+  {
+    "cmd": [],
+    "name": "Checkout flutter/flutter"
+  },
+  {
+    "cmd": [
+      "python3",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/flutter",
+      "--url",
+      "https://flutter.googlesource.com/mirrors/flutter"
+    ],
+    "name": "Checkout flutter/flutter.git setup",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "fetch",
+      "origin",
+      "master",
+      "--recurse-submodules",
+      "--progress",
+      "--tags"
+    ],
+    "cwd": "[START_DIR]/flutter",
+    "env": {
+      "PATH": "RECIPE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "Checkout flutter/flutter.git fetch",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/flutter",
+    "infra_step": true,
+    "name": "Checkout flutter/flutter.git checkout",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/flutter",
+    "infra_step": true,
+    "name": "Checkout flutter/flutter.read revision",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@",
+      "@@@SET_BUILD_PROPERTY@got_revision@\"deadbeef\"@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/flutter",
+    "infra_step": true,
+    "name": "Checkout flutter/flutter.git clean",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/flutter",
+    "infra_step": true,
+    "name": "Checkout flutter/flutter.submodule sync",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/flutter",
+    "infra_step": true,
+    "name": "Checkout flutter/flutter.submodule update",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/flutter",
+    "infra_step": true,
+    "name": "git rev-parse"
+  },
+  {
+    "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[START_DIR]/cloudkms",
+      "-ensure-file",
+      "infra/tools/luci/cloudkms/${platform} latest",
+      "-max-threads",
+      "0",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "cwd": "[START_DIR]/flutter/dev/conductor",
+    "env": {
+      "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
+      "GIT_BRANCH": "",
+      "LUCI_BRANCH": "",
+      "LUCI_CI": "True",
+      "LUCI_PR": "",
+      "OS": "linux",
+      "PUB_CACHE": "[START_DIR]/.pub-cache",
+      "REVISION": "",
+      "SDK_CHECKOUT_PATH": "[START_DIR]/flutter"
+    },
+    "env_prefixes": {
+      "PATH": [
+        "[START_DIR]/flutter/bin",
+        "[START_DIR]/flutter/bin/cache/dart-sdk/bin"
+      ]
+    },
+    "name": "ensure_installed",
+    "~followup_annotations": [
+      "@@@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\": \"infra/tools/luci/cloudkms/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": [
+      "python3",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "gs://flutter_configs/pub-roller-github-token.encrypted",
+      "[CLEANUP]/pub-roller-github-token.encrypted"
+    ],
+    "cwd": "[START_DIR]/flutter/dev/conductor",
+    "env": {
+      "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
+      "GIT_BRANCH": "",
+      "LUCI_BRANCH": "",
+      "LUCI_CI": "True",
+      "LUCI_PR": "",
+      "OS": "linux",
+      "PUB_CACHE": "[START_DIR]/.pub-cache",
+      "REVISION": "",
+      "SDK_CHECKOUT_PATH": "[START_DIR]/flutter"
+    },
+    "env_prefixes": {
+      "PATH": [
+        "[START_DIR]/flutter/bin",
+        "[START_DIR]/flutter/bin/cache/dart-sdk/bin"
+      ]
+    },
+    "infra_step": true,
+    "name": "gsutil download"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cloudkms/cloudkms",
+      "decrypt",
+      "-input",
+      "[CLEANUP]/pub-roller-github-token.encrypted",
+      "-output",
+      "[START_DIR]/flutter/dev/conductor/token.txt",
+      "projects/flutter-infra/locations/global/keyRings/luci/cryptoKeys/flutter-infra"
+    ],
+    "cwd": "[START_DIR]/flutter/dev/conductor",
+    "env": {
+      "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
+      "GIT_BRANCH": "",
+      "LUCI_BRANCH": "",
+      "LUCI_CI": "True",
+      "LUCI_PR": "",
+      "OS": "linux",
+      "PUB_CACHE": "[START_DIR]/.pub-cache",
+      "REVISION": "",
+      "SDK_CHECKOUT_PATH": "[START_DIR]/flutter"
+    },
+    "env_prefixes": {
+      "PATH": [
+        "[START_DIR]/flutter/bin",
+        "[START_DIR]/flutter/bin/cache/dart-sdk/bin"
+      ]
+    },
+    "name": "cloudkms get key"
+  },
+  {
+    "cmd": [
+      "[START_DIR]/flutter/dev/conductor/bin/packages_autoroller",
+      "--token",
+      "[START_DIR]/flutter/dev/conductor/token.txt",
+      "--mirror-remote",
+      "https://github.com/fluttergithubbot/flutter.git"
+    ],
+    "cwd": "[START_DIR]/flutter/dev/conductor",
+    "env": {
+      "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
+      "GIT_BRANCH": "",
+      "LUCI_BRANCH": "",
+      "LUCI_CI": "True",
+      "LUCI_PR": "",
+      "OS": "linux",
+      "PUB_CACHE": "[START_DIR]/.pub-cache",
+      "REVISION": "",
+      "SDK_CHECKOUT_PATH": "[START_DIR]/flutter"
+    },
+    "env_prefixes": {
+      "PATH": [
+        "[START_DIR]/flutter/bin",
+        "[START_DIR]/flutter/bin/cache/dart-sdk/bin"
+      ]
+    },
+    "name": "run roll-packages script"
+  },
+  {
+    "name": "$result"
+  }
+]
\ No newline at end of file
diff --git a/recipes/pub_autoroller/pub_autoroller.py b/recipes/pub_autoroller/pub_autoroller.py
new file mode 100644
index 0000000..ba7c45e
--- /dev/null
+++ b/recipes/pub_autoroller/pub_autoroller.py
@@ -0,0 +1,63 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+DEPS = [
+    'flutter/flutter_deps',
+    'flutter/kms',
+    'flutter/repo_util',
+    'recipe_engine/context',
+    'recipe_engine/path',
+    'recipe_engine/platform',
+    'recipe_engine/properties',
+    'recipe_engine/step',
+]
+
+
+def RunSteps(api):
+  assert api.platform.is_linux, 'This recipe should only be run once per commit, on Linux'
+  checkout_path = api.path['start_dir'].join('flutter')
+  api.repo_util.checkout(
+      'flutter',
+      checkout_path=checkout_path,
+      # If either of these are None, repo_util.checkout will fall back to sane
+      # default
+      url=api.properties.get('git_url'),
+      ref=api.properties.get('git_ref'),
+  )
+  env, env_prefixes = api.repo_util.flutter_environment(checkout_path)
+  api.flutter_deps.required_deps(
+      env,
+      env_prefixes,
+      api.properties.get('dependencies', []),
+  )
+  conductor_dir = checkout_path.join('dev', 'conductor')
+  with api.context(env=env, env_prefixes=env_prefixes, cwd=conductor_dir):
+    roll_packages(api, conductor_dir)
+
+
+def roll_packages(api, conductor_dir):
+  token_file = conductor_dir.join('token.txt')
+  api.kms.get_secret('pub-roller-github-token.encrypted', token_file)
+  autoroll_script = conductor_dir.join(
+      'bin',
+      'packages_autoroller',
+  )
+  # This should be an https url an ssh url cannot be authenticated via an
+  # access token
+  mirror_remote = api.properties.get(
+      'mirror_remote', 'https://github.com/fluttergithubbot/flutter.git')
+  api.step('run roll-packages script', [
+      autoroll_script,
+      '--token',
+      token_file,
+      '--mirror-remote',
+      mirror_remote,
+  ])
+
+
+def GenTests(api):
+  yield api.test(
+      'roll',
+      api.repo_util.flutter_environment_data(),
+  )