[cipd] Auto-generate the full list of packages from cipd.ensure

Change-Id: Ibf5e2f466f675c4594dae019e04716f1897efd7b
Reviewed-on: https://skia-review.googlesource.com/c/buildbot/+/254180
Commit-Queue: Eric Boren <borenet@google.com>
Reviewed-by: Ravi Mistry <rmistry@google.com>
Reviewed-by: Ben Wagner aka dogben <benjaminwagner@google.com>
diff --git a/autoroll/go/repo_manager/pre_upload_steps.go b/autoroll/go/repo_manager/pre_upload_steps.go
index 283a045..45e7db5 100644
--- a/autoroll/go/repo_manager/pre_upload_steps.go
+++ b/autoroll/go/repo_manager/pre_upload_steps.go
@@ -221,7 +221,7 @@
 		if k == "PATH" {
 			// Construct PATH by adding protoc and the required PATH
 			// entries for Go on to the existing PATH.
-			v = path.Join(protocRoot, cipd.PkgProtoc.Dest, "bin") + ":" + v + ":" + os.Getenv("PATH")
+			v = path.Join(protocRoot, cipd.PkgProtoc.Path, "bin") + ":" + v + ":" + os.Getenv("PATH")
 		}
 		envSlice = append(envSlice, fmt.Sprintf("%s=%s", k, v))
 	}
diff --git a/go/cipd/asset_versions_gen.go b/go/cipd/asset_versions_gen.go
index ac3060c..47020aa 100644
--- a/go/cipd/asset_versions_gen.go
+++ b/go/cipd/asset_versions_gen.go
@@ -2,10 +2,90 @@
 
 package cipd
 
-var PKG_VERSIONS_FROM_ASSETS = map[string]string{
-	"gcloud_linux": "14",
-	"go":           "7",
-	"go_win":       "0",
-	"node":         "0",
-	"protoc":       "0",
+var PACKAGES = map[string]*Package{
+	"infra/git/${platform}": {
+		Path:    "cipd_bin_packages",
+		Name:    "infra/git/${platform}",
+		Version: "version:2.23.0.chromium16",
+	},
+	"infra/gsutil": {
+		Path:    "cipd_bin_packages",
+		Name:    "infra/gsutil",
+		Version: "version:4.28",
+	},
+	"infra/python/cpython/${platform}": {
+		Path:    "cipd_bin_packages",
+		Name:    "infra/python/cpython/${platform}",
+		Version: "version:2.7.14.chromium14",
+	},
+	"infra/tools/cipd/${os}-${arch}": {
+		Path:    ".",
+		Name:    "infra/tools/cipd/${os}-${arch}",
+		Version: "latest",
+	},
+	"infra/tools/git/${platform}": {
+		Path:    "cipd_bin_packages",
+		Name:    "infra/tools/git/${platform}",
+		Version: "git_revision:0275b342af7f4ef18f4513f80d3b0e5c1bb3fb6c",
+	},
+	"infra/tools/luci-auth/${platform}": {
+		Path:    "cipd_bin_packages",
+		Name:    "infra/tools/luci-auth/${platform}",
+		Version: "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e",
+	},
+	"infra/tools/luci/git-credential-luci/${platform}": {
+		Path:    "cipd_bin_packages",
+		Name:    "infra/tools/luci/git-credential-luci/${platform}",
+		Version: "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e",
+	},
+	"infra/tools/luci/isolate/${platform}": {
+		Path:    "cipd_bin_packages",
+		Name:    "infra/tools/luci/isolate/${platform}",
+		Version: "git_revision:6759c8c87f5b20e96215db107ec1b050ade347bd",
+	},
+	"infra/tools/luci/isolated/${platform}": {
+		Path:    "cipd_bin_packages",
+		Name:    "infra/tools/luci/isolated/${platform}",
+		Version: "git_revision:6759c8c87f5b20e96215db107ec1b050ade347bd",
+	},
+	"infra/tools/luci/kitchen/${platform}": {
+		Path:    ".",
+		Name:    "infra/tools/luci/kitchen/${platform}",
+		Version: "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc",
+	},
+	"infra/tools/luci/swarming/${platform}": {
+		Path:    "cipd_bin_packages",
+		Name:    "infra/tools/luci/swarming/${platform}",
+		Version: "git_revision:6759c8c87f5b20e96215db107ec1b050ade347bd",
+	},
+	"infra/tools/luci/vpython/${platform}": {
+		Path:    "cipd_bin_packages",
+		Name:    "infra/tools/luci/vpython/${platform}",
+		Version: "git_revision:96f81e737868d43124b4661cf1c325296ca04944",
+	},
+	"skia/bots/gcloud_linux": {
+		Path:    "gcloud_linux",
+		Name:    "skia/bots/gcloud_linux",
+		Version: "version:14",
+	},
+	"skia/bots/go": {
+		Path:    "go",
+		Name:    "skia/bots/go",
+		Version: "version:7",
+	},
+	"skia/bots/go_win": {
+		Path:    "go_win",
+		Name:    "skia/bots/go_win",
+		Version: "version:0",
+	},
+	"skia/bots/node": {
+		Path:    "node",
+		Name:    "skia/bots/node",
+		Version: "version:0",
+	},
+	"skia/bots/protoc": {
+		Path:    "protoc",
+		Name:    "skia/bots/protoc",
+		Version: "version:0",
+	},
 }
diff --git a/go/cipd/cipd.go b/go/cipd/cipd.go
index c36893d..bbb5b18 100644
--- a/go/cipd/cipd.go
+++ b/go/cipd/cipd.go
@@ -13,6 +13,7 @@
 
 	"go.chromium.org/luci/cipd/client/cipd"
 	"go.chromium.org/luci/cipd/common"
+	"go.skia.org/infra/go/skerr"
 	"go.skia.org/infra/go/sklog"
 )
 
@@ -23,17 +24,22 @@
 
 var (
 	// CIPD package for the Go installation.
-	PkgGo = &Package{
-		Dest:    "go",
-		Name:    "skia/bots/go",
-		Version: VersionTag(PKG_VERSIONS_FROM_ASSETS["go"]),
-	}
+	PkgGo = MustGetPackage("skia/bots/go")
 
 	// CIPD package containing the Google Protocol Buffer compiler.
-	PkgProtoc = &Package{
-		Dest:    "protoc",
-		Name:    "skia/bots/protoc",
-		Version: VersionTag(PKG_VERSIONS_FROM_ASSETS["protoc"]),
+	PkgProtoc = MustGetPackage("skia/bots/protoc")
+
+	// CIPD packages required for using Git.
+	PkgsGit = []*Package{
+		MustGetPackage("infra/git/${platform}"),
+		MustGetPackage("infra/tools/git/${platform}"),
+		MustGetPackage("infra/tools/luci/git-credential-luci/${platform}"),
+	}
+
+	// CIPD packages required for using Python.
+	PkgsPython = []*Package{
+		MustGetPackage("infra/python/cpython/${platform}"),
+		MustGetPackage("infra/tools/luci/vpython/${platform}"),
 	}
 )
 
@@ -44,15 +50,35 @@
 
 // Package describes a CIPD package.
 type Package struct {
-	// Relative path within the root dir to install the package.
-	Dest string
-
 	// Name of the package.
-	Name string
+	Name string `json:"name"`
+
+	// Relative path within the root dir to install the package.
+	Path string `json:"path"`
 
 	// Version of the package. See the CIPD docs for valid version strings:
 	// https://godoc.org/go.chromium.org/luci/cipd/common#ValidateInstanceVersion
-	Version string
+	Version string `json:"version"`
+}
+
+// GetPackage returns the definition for the package with the given name, or an
+// error if the package does not exist in the registry.
+func GetPackage(pkg string) (*Package, error) {
+	rv, ok := PACKAGES[pkg]
+	if !ok {
+		return nil, skerr.Fmt("Unknown CIPD package %q", pkg)
+	}
+	return rv, nil
+}
+
+// MustGetPackage returns the definition for the package with the given name.
+// Panics if the package does not exist in the registry.
+func MustGetPackage(pkg string) *Package {
+	rv, err := GetPackage(pkg)
+	if err != nil {
+		sklog.Fatal(err)
+	}
+	return rv
 }
 
 // Run "cipd ensure" to get the correct packages in the given location. Note
@@ -105,7 +131,7 @@
 			return fmt.Errorf("Failed to resolve package version %q @ %q: %s", pkg.Name, pkg.Version, err)
 		}
 		sklog.Infof("Installing version %s (from %s) of %s", pin.InstanceID, pkg.Version, pkg.Name)
-		pkgs[pkg.Dest] = common.PinSlice{pin}
+		pkgs[pkg.Path] = common.PinSlice{pin}
 	}
 	// This means use as many threads as CPUs. (Prior to
 	// https://chromium-review.googlesource.com/c/infra/luci/luci-go/+/1848212,
diff --git a/go/cipd/gen_versions.go b/go/cipd/gen_versions.go
index fd9c3b8..b2fa657 100644
--- a/go/cipd/gen_versions.go
+++ b/go/cipd/gen_versions.go
@@ -9,26 +9,31 @@
 import (
 	"context"
 	"fmt"
+	"io"
 	"io/ioutil"
 	"os"
 	"path"
+	"path/filepath"
 	"runtime"
 	"sort"
 	"strings"
 
+	"go.chromium.org/luci/cipd/client/cipd/ensure"
+	"go.skia.org/infra/go/cipd"
 	"go.skia.org/infra/go/exec"
 	"go.skia.org/infra/go/sklog"
+	"go.skia.org/infra/go/util"
 )
 
 const (
 	TARGET_FILE = "asset_versions_gen.go"
-	TMPL        = `// Code generated by "go run gen_versions.go"; DO NOT EDIT
+	HEADER      = `// Code generated by "go run gen_versions.go"; DO NOT EDIT
 
 package cipd
 
-var PKG_VERSIONS_FROM_ASSETS = map[string]string{
-%s}
+var PACKAGES = map[string]*Package{
 `
+	FOOTER = `}`
 )
 
 func main() {
@@ -42,28 +47,75 @@
 	if err != nil {
 		sklog.Fatal(err)
 	}
-	assets := make(map[string]string, len(entries))
+	pkgs := map[string]*cipd.Package{}
 	for _, e := range entries {
 		if e.IsDir() {
 			contents, err := ioutil.ReadFile(path.Join(assetsDir, e.Name(), "VERSION"))
 			if err == nil {
-				assets[e.Name()] = strings.TrimSpace(string(contents))
+				name := e.Name()
+				fullName := fmt.Sprintf("skia/bots/%s", name)
+				pkgs[fullName] = &cipd.Package{
+					Path:    name,
+					Name:    fullName,
+					Version: cipd.VersionTag(strings.TrimSpace(string(contents))),
+				}
 			} else if !os.IsNotExist(err) {
 				sklog.Fatal(err)
 			}
 		}
 	}
 
-	assetLines := make([]string, 0, len(assets))
-	for name, version := range assets {
-		line := fmt.Sprintf("\t\"%s\": \"%s\",\n", name, version)
-		assetLines = append(assetLines, line)
+	// Read packages from cipd.ensure.
+	var ensureFile *ensure.File
+	if err := util.WithReadFile(filepath.Join(rootDir, "cipd.ensure"), func(r io.Reader) error {
+		f, err := ensure.ParseFile(r)
+		if err == nil {
+			ensureFile = f
+		}
+		return err
+	}); err != nil {
+		sklog.Fatal(err)
 	}
-	sort.Strings(assetLines)
-	assetsStr := strings.Join(assetLines, "")
-	fileContents := []byte(fmt.Sprintf(TMPL, assetsStr))
+	for subdir, pkgSlice := range ensureFile.PackagesBySubdir {
+		if subdir == "" {
+			subdir = "."
+		}
+		for _, pkg := range pkgSlice {
+			pkgs[pkg.PackageTemplate] = &cipd.Package{
+				Path:    subdir,
+				Name:    pkg.PackageTemplate,
+				Version: pkg.UnresolvedVersion,
+			}
+		}
+	}
+
+	// Write the file.
+	pkgNames := make([]string, 0, len(pkgs))
+	for name := range pkgs {
+		pkgNames = append(pkgNames, name)
+	}
+	sort.Strings(pkgNames)
 	targetFile := path.Join(pkgDir, TARGET_FILE)
-	if err := ioutil.WriteFile(targetFile, fileContents, os.ModePerm); err != nil {
+	if err := util.WithWriteFile(targetFile, func(w io.Writer) error {
+		_, err := w.Write([]byte(HEADER))
+		if err != nil {
+			return err
+		}
+		for _, name := range pkgNames {
+			pkg := pkgs[name]
+			_, err := fmt.Fprintf(w, fmt.Sprintf(`	"%s": &Package{
+		Path: "%s",
+		Name: "%s",
+		Version: "%s",
+	},
+`, name, pkg.Path, pkg.Name, pkg.Version))
+			if err != nil {
+				return err
+			}
+		}
+		_, err = w.Write([]byte(FOOTER))
+		return err
+	}); err != nil {
 		sklog.Fatal(err)
 	}
 	if _, err := exec.RunCwd(context.Background(), ".", "gofmt", "-s", "-w", targetFile); err != nil {
diff --git a/go/go_install/go_install.go b/go/go_install/go_install.go
index 4fc3718..907a719 100644
--- a/go/go_install/go_install.go
+++ b/go/go_install/go_install.go
@@ -23,7 +23,7 @@
 	if err := cipd.Ensure(ctx, client, cipdRoot, pkgs...); err != nil {
 		return "", nil, fmt.Errorf("Failed to ensure Go CIPD package: %s", err)
 	}
-	goRoot := path.Join(cipdRoot, cipd.PkgGo.Dest, "go")
+	goRoot := path.Join(cipdRoot, cipd.PkgGo.Path, "go")
 	goBin := path.Join(goRoot, "bin")
 	return path.Join(goBin, "go"), map[string]string{
 		"GO111MODULE": "on",
diff --git a/infra/bots/gen_tasks.go b/infra/bots/gen_tasks.go
index f0745eb..d8668f5 100644
--- a/infra/bots/gen_tasks.go
+++ b/infra/bots/gen_tasks.go
@@ -11,16 +11,13 @@
 import (
 	"encoding/json"
 	"fmt"
-	"io"
 	"path"
 	"path/filepath"
 	"runtime"
 	"strings"
 	"time"
 
-	"go.chromium.org/luci/cipd/client/cipd/ensure"
 	"go.skia.org/infra/go/sklog"
-	"go.skia.org/infra/go/util"
 	"go.skia.org/infra/task_scheduler/go/specs"
 )
 
@@ -28,8 +25,6 @@
 	BUILD_TASK_DRIVERS_NAME = "Housekeeper-PerCommit-BuildTaskDrivers"
 	BUNDLE_RECIPES_NAME     = "Housekeeper-PerCommit-BundleRecipes"
 
-	CIPD_ENSURE_FILE = "cipd.ensure"
-
 	DEFAULT_OS       = DEFAULT_OS_LINUX
 	DEFAULT_OS_LINUX = "Debian-9.8"
 	DEFAULT_OS_WIN   = "Windows-2016Server-14393"
@@ -69,13 +64,6 @@
 		"Infra-Experimental-Small-Win",
 	}
 
-	// CIPD packages used in Swarming tasks. Defined in cipd.ensure.
-	CIPD_PKGS_PYTHON           []*specs.CipdPackage
-	CIPD_PKGS_KITCHEN          []*specs.CipdPackage
-	CIPD_PKGS_GIT              []*specs.CipdPackage
-	CIPD_PKGS_GSUTIL           []*specs.CipdPackage
-	CIPD_PKGS_SWARMING_ISOLATE []*specs.CipdPackage
-
 	CACHES_GO = []*specs.Cache{
 		{
 			Name: "go_cache",
@@ -139,7 +127,7 @@
 // Apply the default CIPD packages.
 func cipd(pkgs []*specs.CipdPackage) []*specs.CipdPackage {
 	// We also need Git.
-	rv := append(CIPD_PKGS_KITCHEN, CIPD_PKGS_GIT...)
+	rv := append(specs.CIPD_PKGS_KITCHEN, specs.CIPD_PKGS_GIT...)
 	return append(rv, pkgs...)
 }
 
@@ -167,7 +155,7 @@
 // bundleRecipes generates the task to bundle and isolate the recipes.
 func bundleRecipes(b *specs.TasksCfgBuilder) string {
 	b.MustAddTask(BUNDLE_RECIPES_NAME, &specs.TaskSpec{
-		CipdPackages: append(CIPD_PKGS_GIT, CIPD_PKGS_PYTHON...),
+		CipdPackages: append(specs.CIPD_PKGS_GIT, specs.CIPD_PKGS_PYTHON...),
 		Command: []string{
 			"/bin/bash", "buildbot/infra/bots/bundle_recipes.sh", specs.PLACEHOLDER_ISOLATED_OUTDIR,
 		},
@@ -197,7 +185,7 @@
 	name := fmt.Sprintf("%s-%s-%s", BUILD_TASK_DRIVERS_NAME, os, arch)
 	b.MustAddTask(name, &specs.TaskSpec{
 		Caches:       CACHES_GO,
-		CipdPackages: append(CIPD_PKGS_GIT, b.MustGetCipdPackageFromAsset("go")),
+		CipdPackages: append(specs.CIPD_PKGS_GIT, b.MustGetCipdPackageFromAsset("go")),
 		Command: []string{
 			"/bin/bash", "buildbot/infra/bots/build_task_drivers.sh", specs.PLACEHOLDER_ISOLATED_OUTDIR,
 		},
@@ -221,7 +209,7 @@
 // kitchenTask returns a specs.TaskSpec instance which uses Kitchen to run a
 // recipe.
 func kitchenTask(name, recipe, isolate, serviceAccount string, dimensions []string, extraProps map[string]string, outputDir string) *specs.TaskSpec {
-	cipd := append([]*specs.CipdPackage{}, CIPD_PKGS_KITCHEN...)
+	cipd := append([]*specs.CipdPackage{}, specs.CIPD_PKGS_KITCHEN...)
 	properties := map[string]string{
 		"buildername":   name,
 		"swarm_out_dir": specs.PLACEHOLDER_ISOLATED_OUTDIR,
@@ -267,18 +255,18 @@
 		machineType = MACHINE_TYPE_LARGE
 	}
 	task := kitchenTask(name, "swarm_infra", "whole_repo.isolate", SERVICE_ACCOUNT_COMPILE, linuxGceDimensions(machineType), nil, OUTPUT_NONE)
-	task.CipdPackages = append(task.CipdPackages, CIPD_PKGS_GIT...)
+	task.CipdPackages = append(task.CipdPackages, specs.CIPD_PKGS_GIT...)
 	task.CipdPackages = append(task.CipdPackages, b.MustGetCipdPackageFromAsset("go"))
 	task.Caches = append(task.Caches, CACHES_GO...)
 	task.CipdPackages = append(task.CipdPackages, b.MustGetCipdPackageFromAsset("node"))
-	task.CipdPackages = append(task.CipdPackages, CIPD_PKGS_GSUTIL...)
+	task.CipdPackages = append(task.CipdPackages, specs.CIPD_PKGS_GSUTIL...)
 	if strings.Contains(name, "Large") || strings.Contains(name, "Build") {
 		task.CipdPackages = append(task.CipdPackages, b.MustGetCipdPackageFromAsset("protoc"))
 	}
 
 	// Cloud datastore tests are assumed to be marked as 'Large'
 	if strings.Contains(name, "Large") || strings.Contains(name, "Race") {
-		task.CipdPackages = append(task.CipdPackages, CIPD_PKGS_SWARMING_ISOLATE...)
+		task.CipdPackages = append(task.CipdPackages, specs.CIPD_PKGS_ISOLATE...)
 		task.CipdPackages = append(task.CipdPackages, b.MustGetCipdPackageFromAsset("gcloud_linux"))
 	}
 
@@ -316,7 +304,7 @@
 			Path: "cache/git_cache",
 		},
 	}...)
-	task.CipdPackages = append(task.CipdPackages, CIPD_PKGS_GIT...)
+	task.CipdPackages = append(task.CipdPackages, specs.CIPD_PKGS_GIT...)
 	task.CipdPackages = append(task.CipdPackages, &specs.CipdPackage{
 		Name:    "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",
 		Path:    "recipe_bundle",
@@ -328,9 +316,9 @@
 }
 
 func experimental(b *specs.TasksCfgBuilder, name string) string {
-	cipd := append([]*specs.CipdPackage{}, CIPD_PKGS_GIT...)
-	cipd = append(cipd, CIPD_PKGS_GSUTIL...)
-	cipd = append(cipd, CIPD_PKGS_PYTHON...)
+	cipd := append([]*specs.CipdPackage{}, specs.CIPD_PKGS_GIT...)
+	cipd = append(cipd, specs.CIPD_PKGS_GSUTIL...)
+	cipd = append(cipd, specs.CIPD_PKGS_PYTHON...)
 	cipd = append(cipd, b.MustGetCipdPackageFromAsset("node"))
 
 	machineType := MACHINE_TYPE_MEDIUM
@@ -377,7 +365,7 @@
 }
 
 func updateGoDeps(b *specs.TasksCfgBuilder, name string) string {
-	cipd := append([]*specs.CipdPackage{}, CIPD_PKGS_GIT...)
+	cipd := append([]*specs.CipdPackage{}, specs.CIPD_PKGS_GIT...)
 	cipd = append(cipd, b.MustGetCipdPackageFromAsset("go"))
 	cipd = append(cipd, b.MustGetCipdPackageFromAsset("protoc"))
 
@@ -450,72 +438,8 @@
 	})
 }
 
-// Load the CIPD package versions from the cipd.ensure file.
-func loadCIPD() error {
-	root, err := specs.GetCheckoutRoot()
-	if err != nil {
-		return err
-	}
-	ensureFilePath := filepath.Join(root, CIPD_ENSURE_FILE)
-	var ensureFile *ensure.File
-	if err := util.WithReadFile(ensureFilePath, func(r io.Reader) error {
-		f, err := ensure.ParseFile(r)
-		if err == nil {
-			ensureFile = f
-		}
-		return err
-	}); err != nil {
-		return err
-	}
-	pkgs := map[string]*specs.CipdPackage{}
-	for subdir, pkgSlice := range ensureFile.PackagesBySubdir {
-		if subdir == "" {
-			subdir = "."
-		}
-		for _, pkg := range pkgSlice {
-			pkgs[pkg.PackageTemplate] = &specs.CipdPackage{
-				Name:    pkg.PackageTemplate,
-				Path:    subdir,
-				Version: pkg.UnresolvedVersion,
-			}
-		}
-	}
-	pkg := func(name string) *specs.CipdPackage {
-		rv, ok := pkgs[name]
-		if !ok {
-			err = fmt.Errorf("Required package %q not found in %s", name, ensureFilePath)
-		}
-		return rv
-	}
-	CIPD_PKGS_PYTHON = []*specs.CipdPackage{
-		pkg("infra/python/cpython/${platform}"),
-		pkg("infra/tools/luci/vpython/${platform}"),
-	}
-	CIPD_PKGS_KITCHEN = append([]*specs.CipdPackage{
-		pkg("infra/tools/luci/kitchen/${platform}"),
-		pkg("infra/tools/luci-auth/${platform}"),
-	}, CIPD_PKGS_PYTHON...)
-	CIPD_PKGS_GIT = []*specs.CipdPackage{
-		pkg("infra/git/${platform}"),
-		pkg("infra/tools/git/${platform}"),
-		pkg("infra/tools/luci/git-credential-luci/${platform}"),
-	}
-	CIPD_PKGS_GSUTIL = []*specs.CipdPackage{
-		pkg("infra/gsutil"),
-	}
-	CIPD_PKGS_SWARMING_ISOLATE = []*specs.CipdPackage{
-		pkg("infra/tools/luci/isolate/${platform}"),
-		pkg("infra/tools/luci/isolated/${platform}"),
-	}
-	return err
-}
-
 // Regenerate the tasks.json file.
 func main() {
-	if err := loadCIPD(); err != nil {
-		sklog.Fatal(err)
-	}
-
 	b := specs.MustNewTasksCfgBuilder()
 
 	// Create Tasks and Jobs.
diff --git a/task_scheduler/go/specs/specs.go b/task_scheduler/go/specs/specs.go
index 21ce256..0d426b0 100644
--- a/task_scheduler/go/specs/specs.go
+++ b/task_scheduler/go/specs/specs.go
@@ -10,6 +10,7 @@
 	"strings"
 	"time"
 
+	"go.skia.org/infra/go/cipd"
 	"go.skia.org/infra/go/periodic"
 	"go.skia.org/infra/go/util"
 	"go.skia.org/infra/task_scheduler/go/types"
@@ -58,6 +59,21 @@
 )
 
 var (
+	// CIPD packages which may be used in tasks.
+	CIPD_PKGS_GIT     []*CipdPackage = cipd.PkgsGit
+	CIPD_PKGS_GSUTIL                 = []*CipdPackage{cipd.MustGetPackage("infra/gsutil")}
+	CIPD_PKGS_ISOLATE                = []*CipdPackage{
+		cipd.MustGetPackage("infra/tools/luci/isolate/${platform}"),
+		cipd.MustGetPackage("infra/tools/luci/isolated/${platform}"),
+	}
+	CIPD_PKGS_PYTHON  []*CipdPackage = cipd.PkgsPython
+	CIPD_PKGS_KITCHEN                = append([]*CipdPackage{
+		cipd.MustGetPackage("infra/tools/luci/kitchen/${platform}"),
+		cipd.MustGetPackage("infra/tools/luci-auth/${platform}"),
+	}, CIPD_PKGS_PYTHON...)
+
+	// Variable placeholders; these are replaced with the actual value
+	// at task triggering time.
 	PLACEHOLDER_BUILDBUCKET_BUILD_ID = fmt.Sprintf(VARIABLE_SYNTAX, VARIABLE_BUILDBUCKET_BUILD_ID)
 	PLACEHOLDER_CODEREVIEW_SERVER    = fmt.Sprintf(VARIABLE_SYNTAX, VARIABLE_CODEREVIEW_SERVER)
 	PLACEHOLDER_ISSUE                = fmt.Sprintf(VARIABLE_SYNTAX, VARIABLE_ISSUE)
@@ -336,11 +352,9 @@
 
 // CipdPackage is a struct representing a CIPD package which needs to be
 // installed on a bot for a particular task.
-type CipdPackage struct {
-	Name    string `json:"name"`
-	Path    string `json:"path"`
-	Version string `json:"version"`
-}
+// TODO(borenet): Are there any downsides to using an alias rather than a new
+// type here?
+type CipdPackage = cipd.Package
 
 // JobSpec is a struct which describes a set of TaskSpecs to run as part of a
 // larger effort.