├── .bazelignore ├── .bazelrc ├── .bazelversion ├── .gitignore ├── BUILD.bazel ├── README.md ├── WORKSPACE ├── add-go-dep.sh ├── bazel ├── calculate ├── BUILD.bazel ├── calculator.go ├── output.go └── query.go ├── cmd ├── BUILD.bazel ├── calculate.go ├── compare.go └── root.go ├── compare ├── BUILD.bazel └── compare.go ├── go.mod ├── go.sum ├── godeps_macro.bzl ├── main.go ├── protos ├── BUILD.bazel └── query.proto └── query ├── BUILD.bazel └── utils.go /.bazelignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/purkhusid/biff/cc8851e28b8faf43bc02023f16ad9c10289db0fb/.bazelignore -------------------------------------------------------------------------------- /.bazelrc: -------------------------------------------------------------------------------- 1 | try-import user.bazelrc -------------------------------------------------------------------------------- /.bazelversion: -------------------------------------------------------------------------------- 1 | 3.2.0 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /bazel-* 2 | .mypy_cache 3 | .vscode 4 | user.bazelrc -------------------------------------------------------------------------------- /BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_path") 2 | load("@bazel_gazelle//:def.bzl", "gazelle") 3 | 4 | go_library( 5 | name = "go_default_library", 6 | srcs = ["main.go"], 7 | importpath = "github.com/purkhusid/biff", 8 | visibility = ["//visibility:public"], 9 | deps = ["//cmd:go_default_library"], 10 | ) 11 | 12 | go_binary( 13 | name = "biff", 14 | embed = [":go_default_library"], 15 | visibility = ["//visibility:public"], 16 | ) 17 | 18 | # gazelle:prefix github.com/purkhusid/biff 19 | # gazelle:build_file_name BUILD.bazel 20 | gazelle(name = "gazelle") 21 | 22 | #This target is used for Go IDE tooling 23 | go_path( 24 | name = "gopath", 25 | mode = "link", 26 | deps = [ 27 | "//:biff", 28 | ], 29 | ) 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Biff - A Bazel VCS diffing tool 2 | 3 | Biff tries to answer a simple question in your Bazel monorepository. 4 | 5 | _What Bazel targets were affected by my VCS changeset?_ 6 | 7 | Bazel can't answer this question because it does not have any information about your VCS setup. 8 | 9 | ## How do I use this thing 10 | 11 | You can run the tool straight from this repo by running 12 | 13 | `./bazel run //:biff -- ` 14 | 15 | or you can build the tool and copy the binary from `bazel-bin` 16 | 17 | `./bazel build //:biff` 18 | 19 | `cp bazel-bin/biff_/biff ` 20 | 21 | `biff ` 22 | 23 | ### Commands 24 | 25 | #### Calculate 26 | 27 | Calculates the sha256 checksum for each target in the graph and outputs it to a file. 28 | This file can then be diffed against another output with the compare command 29 | 30 | Usage: 31 | ``` 32 | biff calculate [flags] 33 | ``` 34 | Flags: 35 | ``` 36 | --bazel string Location of Bazel executable. By default uses bazel from path 37 | -h, --help help for calculate 38 | --out string Where the output should be written to (Required) 39 | --workspace string Path to the workspace root (Required) 40 | ``` 41 | 42 | #### Compare 43 | 44 | Compares two outputs from the calculate command and outputs a file with only the changed targets 45 | 46 | Usage: 47 | ``` 48 | biff compare [flags] 49 | ``` 50 | 51 | Flags: 52 | ``` 53 | -h, --help help for compare 54 | --left string The 'left' side of the comparison (Required) 55 | --out string Where the output should be written to (Required) 56 | --right string The 'right' side of the comparison (Required) 57 | ``` 58 | -------------------------------------------------------------------------------- /WORKSPACE: -------------------------------------------------------------------------------- 1 | workspace( 2 | name = "biff", 3 | ) 4 | 5 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") 6 | load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") 7 | 8 | # Bazel skylib 9 | git_repository( 10 | name = "bazel_skylib", 11 | commit = "327d61b5eaa15c11a868a1f7f3f97cdf07d31c58", 12 | remote = "https://github.com/bazelbuild/bazel-skylib.git", 13 | shallow_since = "1572441481 +0100", 14 | ) 15 | 16 | load("@bazel_skylib//lib:versions.bzl", "versions") 17 | 18 | versions.check( 19 | minimum_bazel_version = "3.2.0", 20 | maximum_bazel_version = "3.2.0", 21 | ) 22 | 23 | # Protobuf dependencies 24 | http_archive( 25 | name = "com_google_protobuf", 26 | sha256 = "761bfffc7d53cd01514fa237ca0d3aba5a3cfd8832a71808c0ccc447174fd0da", 27 | strip_prefix = "protobuf-3.11.1", 28 | urls = ["https://github.com/protocolbuffers/protobuf/releases/download/v3.11.1/protobuf-all-3.11.1.tar.gz"], 29 | ) 30 | 31 | load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps") 32 | 33 | protobuf_deps() 34 | 35 | # Golang rules 36 | http_archive( 37 | name = "io_bazel_rules_go", 38 | sha256 = "a8d6b1b354d371a646d2f7927319974e0f9e52f73a2452d2b3877118169eb6bb", 39 | urls = [ 40 | "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.23.3/rules_go-v0.23.3.tar.gz", 41 | "https://github.com/bazelbuild/rules_go/releases/download/v0.23.3/rules_go-v0.23.3.tar.gz", 42 | ], 43 | ) 44 | 45 | gazelle_git_hash = "b6658b6a47d9f0a06988ff15233168dcc713b536" 46 | 47 | http_archive( 48 | name = "bazel_gazelle", 49 | sha256 = "d1e4aaa733992a1b00084dde808eb2dfe2e325c71e31ed74184376a449b2aeac", 50 | strip_prefix = "bazel-gazelle-%s" % gazelle_git_hash, 51 | type = "zip", 52 | url = "https://github.com/bazelbuild/bazel-gazelle/archive/%s.zip" % gazelle_git_hash, 53 | ) 54 | 55 | load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies") 56 | 57 | go_rules_dependencies() 58 | 59 | go_register_toolchains() 60 | 61 | load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies") 62 | 63 | gazelle_dependencies() 64 | 65 | load("//:godeps_macro.bzl", "go_repositories") 66 | 67 | # gazelle:repository_macro godeps_macro.bzl%go_repositories 68 | go_repositories() 69 | -------------------------------------------------------------------------------- /add-go-dep.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | go get "${1}" 4 | ./bazel run //:gazelle -- update-repos -from_file=go.mod -to_macro=godeps_macro.bzl%go_repositories -------------------------------------------------------------------------------- /bazel: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Copyright 2018 Google Inc. All rights reserved. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | """ 17 | 18 | from contextlib import closing 19 | from distutils.version import LooseVersion 20 | import json 21 | import os 22 | import os.path 23 | import platform 24 | import re 25 | import shutil 26 | import subprocess 27 | import sys 28 | import tempfile 29 | import time 30 | 31 | try: 32 | from urllib.request import urlopen 33 | except ImportError: 34 | # Python 2.x compatibility hack. 35 | from urllib2 import urlopen 36 | 37 | ONE_HOUR = 1 * 60 * 60 38 | 39 | LATEST_PATTERN = re.compile(r"latest(-(?P\d+))?$") 40 | 41 | LAST_GREEN_COMMIT_BASE_PATH = "https://storage.googleapis.com/bazel-untrusted-builds/last_green_commit/" 42 | 43 | LAST_GREEN_COMMIT_PATH_SUFFIXES = {"last_green" : "github.com/bazelbuild/bazel.git/bazel-bazel", "last_downstream_green" : "downstream_pipeline"} 44 | 45 | BAZEL_GCS_PATH_PATTERN = ( 46 | "https://storage.googleapis.com/bazel-builds/artifacts/{platform}/{commit}/bazel" 47 | ) 48 | 49 | SUPPORTED_PLATFORMS = {"linux": "ubuntu1404", "windows": "windows", "darwin": "macos"} 50 | 51 | TOOLS_BAZEL_PATH = "./tools/bazel" 52 | 53 | BAZEL_REAL = "BAZEL_REAL" 54 | 55 | 56 | def decide_which_bazel_version_to_use(): 57 | # Check in this order: 58 | # - env var "USE_BAZEL_VERSION" is set to a specific version. 59 | # - env var "USE_NIGHTLY_BAZEL" or "USE_BAZEL_NIGHTLY" is set -> latest 60 | # nightly. (TODO) 61 | # - env var "USE_CANARY_BAZEL" or "USE_BAZEL_CANARY" is set -> latest 62 | # rc. (TODO) 63 | # - the file workspace_root/tools/bazel exists -> that version. (TODO) 64 | # - workspace_root/.bazelversion exists -> read contents, that version. 65 | # - workspace_root/WORKSPACE contains a version -> that version. (TODO) 66 | # - fallback: latest release 67 | if "USE_BAZEL_VERSION" in os.environ: 68 | return os.environ["USE_BAZEL_VERSION"] 69 | 70 | workspace_root = find_workspace_root() 71 | if workspace_root: 72 | bazelversion_path = os.path.join(workspace_root, ".bazelversion") 73 | if os.path.exists(bazelversion_path): 74 | with open(bazelversion_path, "r") as f: 75 | return f.read().strip() 76 | 77 | return "latest" 78 | 79 | 80 | def find_workspace_root(root=None): 81 | if root is None: 82 | root = os.getcwd() 83 | if os.path.exists(os.path.join(root, "WORKSPACE")): 84 | return root 85 | new_root = os.path.dirname(root) 86 | return find_workspace_root(new_root) if new_root != root else None 87 | 88 | 89 | def resolve_version_label_to_number_or_commit(bazelisk_directory, version): 90 | """Resolves the given label to a released version of Bazel or a commit. 91 | 92 | Args: 93 | bazelisk_directory: string; path to a directory that can store 94 | temporary data for Bazelisk. 95 | version: string; the version label that should be resolved. 96 | Returns: 97 | A (string, bool) tuple that consists of two parts: 98 | 1. the resolved number of a Bazel release (candidate), or the commit 99 | of an unreleased Bazel binary, 100 | 2. An indicator for whether the returned version refers to a commit. 101 | """ 102 | suffix = LAST_GREEN_COMMIT_PATH_SUFFIXES.get(version) 103 | if suffix: 104 | return get_last_green_commit(suffix), True 105 | 106 | if "latest" in version: 107 | match = LATEST_PATTERN.match(version) 108 | if not match: 109 | raise Exception( 110 | 'Invalid version "{}". In addition to using a version ' 111 | 'number such as "0.20.0", you can use values such as ' 112 | '"latest" and "latest-N", with N being a non-negative ' 113 | "integer.".format(version) 114 | ) 115 | 116 | history = get_version_history(bazelisk_directory) 117 | offset = int(match.group("offset") or "0") 118 | return resolve_latest_version(history, offset), False 119 | 120 | return version, False 121 | 122 | 123 | def get_last_green_commit(path_suffix): 124 | return read_remote_text_file(LAST_GREEN_COMMIT_BASE_PATH + path_suffix).strip() 125 | 126 | 127 | def get_releases_json(bazelisk_directory): 128 | """Returns the most recent versions of Bazel, in descending order.""" 129 | releases = os.path.join(bazelisk_directory, "releases.json") 130 | 131 | # Use a cached version if it's fresh enough. 132 | if os.path.exists(releases): 133 | if abs(time.time() - os.path.getmtime(releases)) < ONE_HOUR: 134 | with open(releases, "rb") as f: 135 | try: 136 | return json.loads(f.read().decode("utf-8")) 137 | except ValueError: 138 | print("WARN: Could not parse cached releases.json.") 139 | pass 140 | 141 | with open(releases, "wb") as f: 142 | body = read_remote_text_file("https://api.github.com/repos/bazelbuild/bazel/releases") 143 | f.write(body.encode("utf-8")) 144 | return json.loads(body) 145 | 146 | 147 | def read_remote_text_file(url): 148 | with closing(urlopen(url)) as res: 149 | body = res.read() 150 | try: 151 | return body.decode(res.info().get_content_charset("iso-8859-1")) 152 | except AttributeError: 153 | # Python 2.x compatibility hack 154 | return body.decode(res.info().getparam("charset") or "iso-8859-1") 155 | 156 | 157 | def get_version_history(bazelisk_directory): 158 | ordered = sorted( 159 | ( 160 | LooseVersion(release["tag_name"]) 161 | for release in get_releases_json(bazelisk_directory) 162 | if not release["prerelease"] 163 | ), 164 | reverse=True, 165 | ) 166 | return [str(v) for v in ordered] 167 | 168 | 169 | def resolve_latest_version(version_history, offset): 170 | if offset >= len(version_history): 171 | version = "latest-{}".format(offset) if offset else "latest" 172 | raise Exception( 173 | 'Cannot resolve version "{}": There are only {} Bazel ' 174 | "releases.".format(version, len(version_history)) 175 | ) 176 | 177 | # This only works since we store the history in descending order. 178 | return version_history[offset] 179 | 180 | 181 | def get_operating_system(): 182 | operating_system = platform.system().lower() 183 | if operating_system not in ("linux", "darwin", "windows"): 184 | raise Exception( 185 | 'Unsupported operating system "{}". ' 186 | "Bazel currently only supports Linux, macOS and Windows.".format(operating_system) 187 | ) 188 | return operating_system 189 | 190 | 191 | def determine_bazel_filename(version): 192 | machine = normalized_machine_arch_name() 193 | if machine != "x86_64": 194 | raise Exception( 195 | 'Unsupported machine architecture "{}". Bazel currently only supports x86_64.'.format( 196 | machine 197 | ) 198 | ) 199 | 200 | operating_system = get_operating_system() 201 | 202 | filename_ending = ".exe" if operating_system == "windows" else "" 203 | return "bazel-{}-{}-{}{}".format(version, operating_system, machine, filename_ending) 204 | 205 | 206 | def normalized_machine_arch_name(): 207 | machine = platform.machine().lower() 208 | if machine == "amd64": 209 | machine = "x86_64" 210 | return machine 211 | 212 | 213 | def determine_url(version, is_commit, bazel_filename): 214 | if is_commit: 215 | sys.stderr.write("Using unreleased version at commit {}\n".format(version)) 216 | # No need to validate the platform thanks to determine_bazel_filename(). 217 | return BAZEL_GCS_PATH_PATTERN.format( 218 | platform=SUPPORTED_PLATFORMS[platform.system().lower()], commit=version 219 | ) 220 | 221 | # Split version into base version and optional additional identifier. 222 | # Example: '0.19.1' -> ('0.19.1', None), '0.20.0rc1' -> ('0.20.0', 'rc1') 223 | (version, rc) = re.match(r"(\d*\.\d*(?:\.\d*)?)(rc\d+)?", version).groups() 224 | return "https://releases.bazel.build/{}/{}/{}".format( 225 | version, rc if rc else "release", bazel_filename 226 | ) 227 | 228 | 229 | def download_bazel_into_directory(version, is_commit, directory): 230 | bazel_filename = determine_bazel_filename(version) 231 | url = determine_url(version, is_commit, bazel_filename) 232 | destination_path = os.path.join(directory, bazel_filename) 233 | if not os.path.exists(destination_path): 234 | sys.stderr.write("Downloading {}...\n".format(url)) 235 | with tempfile.NamedTemporaryFile(prefix="bazelisk", dir=directory, delete=False) as t: 236 | with closing(urlopen(url)) as response: 237 | shutil.copyfileobj(response, t) 238 | t.flush() 239 | os.fsync(t.fileno()) 240 | os.rename(t.name, destination_path) 241 | os.chmod(destination_path, 0o755) 242 | return destination_path 243 | 244 | 245 | def get_bazelisk_directory(): 246 | bazelisk_home = os.environ.get("BAZELISK_HOME") 247 | if bazelisk_home is not None: 248 | return bazelisk_home 249 | 250 | operating_system = get_operating_system() 251 | 252 | base_dir = None 253 | 254 | if operating_system == "windows": 255 | base_dir = os.environ.get("LocalAppData") 256 | if base_dir is None: 257 | raise Exception("%LocalAppData% is not defined") 258 | elif operating_system == "darwin": 259 | base_dir = os.environ.get("HOME") 260 | if base_dir is None: 261 | raise Exception("$HOME is not defined") 262 | base_dir = os.path.join(base_dir, "Library/Caches") 263 | elif operating_system == "linux": 264 | base_dir = os.environ.get("XDG_CACHE_HOME") 265 | if base_dir is None: 266 | base_dir = os.environ.get("HOME") 267 | if base_dir is None: 268 | raise Exception("neither $XDG_CACHE_HOME nor $HOME are defined") 269 | base_dir = os.path.join(base_dir, ".cache") 270 | else: 271 | raise Exception("Unsupported operating system '{}'".format(operating_system)) 272 | 273 | return os.path.join(base_dir, "bazelisk") 274 | 275 | 276 | def maybe_makedirs(path): 277 | """ 278 | Creates a directory and its parents if necessary. 279 | """ 280 | try: 281 | os.makedirs(path) 282 | except OSError as e: 283 | if not os.path.isdir(path): 284 | raise e 285 | 286 | def delegate_tools_bazel(bazel_path): 287 | """Match Bazel's own delegation behavior in the builds distributed by most 288 | package managers: use tools/bazel if it's present, executable, and not this 289 | script. 290 | """ 291 | root = find_workspace_root() 292 | if root: 293 | wrapper = os.path.join(root, TOOLS_BAZEL_PATH) 294 | if (os.path.exists(wrapper) and os.access(wrapper, os.X_OK)): 295 | if wrapper != os.path.abspath(__file__): 296 | return wrapper 297 | return None 298 | 299 | 300 | def execute_bazel(bazel_path, argv): 301 | wrapper = delegate_tools_bazel(bazel_path) 302 | if wrapper: 303 | os.putenv(BAZEL_REAL, bazel_path) 304 | bazel_path = wrapper 305 | 306 | # We cannot use close_fds on Windows, so disable it there. 307 | p = subprocess.Popen([bazel_path] + argv, close_fds=os.name != "nt") 308 | while True: 309 | try: 310 | return p.wait() 311 | except KeyboardInterrupt: 312 | # Bazel will also get the signal and terminate. 313 | # We should continue waiting until it does so. 314 | pass 315 | 316 | 317 | def main(argv=None): 318 | if argv is None: 319 | argv = sys.argv 320 | 321 | bazelisk_directory = get_bazelisk_directory() 322 | maybe_makedirs(bazelisk_directory) 323 | 324 | bazel_version = decide_which_bazel_version_to_use() 325 | bazel_version, is_commit = resolve_version_label_to_number_or_commit( 326 | bazelisk_directory, bazel_version 327 | ) 328 | 329 | bazel_directory = os.path.join(bazelisk_directory, "bin") 330 | maybe_makedirs(bazel_directory) 331 | bazel_path = download_bazel_into_directory(bazel_version, is_commit, bazel_directory) 332 | 333 | return execute_bazel(bazel_path, argv[1:]) 334 | 335 | 336 | if __name__ == "__main__": 337 | sys.exit(main()) 338 | -------------------------------------------------------------------------------- /calculate/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "go_default_library", 5 | srcs = [ 6 | "calculator.go", 7 | "output.go", 8 | "query.go", 9 | ], 10 | importpath = "github.com/purkhusid/biff/calculate", 11 | visibility = ["//visibility:public"], 12 | deps = [ 13 | "//protos:go_default_library", 14 | "//query:go_default_library", 15 | "@com_github_golang_protobuf//proto:go_default_library", 16 | ], 17 | ) 18 | -------------------------------------------------------------------------------- /calculate/calculator.go: -------------------------------------------------------------------------------- 1 | package calculate 2 | 3 | import ( 4 | "crypto/sha256" 5 | "fmt" 6 | "io" 7 | "log" 8 | "os" 9 | "path" 10 | "path/filepath" 11 | "strings" 12 | 13 | "github.com/golang/protobuf/proto" 14 | blaze_query "github.com/purkhusid/biff/protos" 15 | "github.com/purkhusid/biff/query" 16 | ) 17 | 18 | type HashedRuleTarget struct { 19 | Rule *blaze_query.Rule 20 | Hash string 21 | } 22 | 23 | // Calculator ... 24 | type Calculator struct { 25 | QueryResult *blaze_query.QueryResult 26 | LabelTargets map[string]*blaze_query.Target 27 | LabelCache map[string]string 28 | } 29 | 30 | // NewCalculator ... 31 | func NewCalculator(queryResult *blaze_query.QueryResult) Calculator { 32 | labelTargets := make(map[string]*blaze_query.Target) 33 | 34 | for _, target := range queryResult.GetTarget() { 35 | targetLabel := query.GetTargetLabel(target) 36 | labelTargets[targetLabel] = target 37 | } 38 | 39 | return Calculator{ 40 | QueryResult: queryResult, 41 | LabelTargets: labelTargets, 42 | LabelCache: make(map[string]string), 43 | } 44 | } 45 | 46 | func (c *Calculator) calculateSourceFileHash(sourceFile *blaze_query.SourceFile) string { 47 | pathToTarget := filepath.Dir(strings.Split(sourceFile.GetLocation(), ":")[0]) 48 | pathFromTargetSplit := strings.Split(strings.Replace(sourceFile.GetName(), "//", "", -1), ":") 49 | pathFromTarget := pathFromTargetSplit[len(pathFromTargetSplit)-1] 50 | combinedPath := path.Join(pathToTarget, pathFromTarget) 51 | 52 | // Get absolute path of file 53 | fileAbsPath, err := filepath.Abs(combinedPath) 54 | if err != nil { 55 | log.Panicf("Failed to get absolute path of %s", combinedPath) 56 | } 57 | 58 | // Open the file for use 59 | file, err := os.Open(fileAbsPath) 60 | if err != nil { 61 | log.Printf("Failed to open %s", fileAbsPath) 62 | c.LabelCache[sourceFile.GetName()] = "" 63 | return "" 64 | } 65 | defer file.Close() 66 | 67 | checksum := sha256.New() 68 | if _, err := io.Copy(checksum, file); err != nil { 69 | log.Panicf("Failed to get checksum of %s", fileAbsPath) 70 | } 71 | 72 | hashString := fmt.Sprintf("%x", checksum.Sum(nil)) 73 | c.LabelCache[sourceFile.GetName()] = hashString 74 | return hashString 75 | } 76 | 77 | func (c *Calculator) calculateInputHash(label string) string { 78 | cachedHash, exists := c.LabelCache[label] 79 | 80 | if exists { 81 | return cachedHash 82 | } 83 | 84 | target, exists := c.LabelTargets[label] 85 | 86 | if !exists { 87 | log.Printf("Did not find target with label %s", label) 88 | return "" 89 | } 90 | 91 | if target.GetRule() != nil { 92 | return c.calculateRuleHash(target.GetRule()) 93 | } 94 | 95 | if target.GetSourceFile() != nil { 96 | return c.calculateSourceFileHash(target.GetSourceFile()) 97 | } 98 | 99 | return "" 100 | } 101 | 102 | func (c *Calculator) calculateRuleHash(rule *blaze_query.Rule) string { 103 | hash := sha256.New() 104 | 105 | // Add the attributes to the hash 106 | attributes := query.GetRuleAttributes(rule) 107 | for _, attribute := range attributes { 108 | attributeName := attribute.GetName() 109 | 110 | // Exclude attributes that include absolute paths 111 | if attributeName != "generator_location" && attributeName != "path" && attributeName != "build_file" { 112 | attributeBytes, err := proto.Marshal(attribute) 113 | 114 | if err != nil { 115 | log.Panicf("Failed to marshal attribute %s on target %s", attribute.GetName(), rule.GetName()) 116 | } 117 | 118 | hash.Write(attributeBytes) 119 | } 120 | } 121 | 122 | for _, input := range rule.GetRuleInput() { 123 | inputHash := c.calculateInputHash(input) 124 | hash.Write([]byte(inputHash)) 125 | } 126 | 127 | hashString := fmt.Sprintf("%x", hash.Sum(nil)) 128 | c.LabelCache[rule.GetName()] = hashString 129 | return hashString 130 | } 131 | 132 | // CalculateHashes ... 133 | func (c *Calculator) CalculateHashes() []HashedRuleTarget { 134 | hashedRuleTargets := make([]HashedRuleTarget, 0) 135 | ruleTargets := query.GetRuleTargets(c.QueryResult) 136 | for _, ruleTarget := range ruleTargets { 137 | ruleHash := c.calculateRuleHash(ruleTarget) 138 | hashedRuleTarget := HashedRuleTarget{ 139 | Rule: ruleTarget, 140 | Hash: ruleHash, 141 | } 142 | hashedRuleTargets = append(hashedRuleTargets, hashedRuleTarget) 143 | } 144 | 145 | return hashedRuleTargets 146 | } 147 | -------------------------------------------------------------------------------- /calculate/output.go: -------------------------------------------------------------------------------- 1 | package calculate 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "log" 7 | "os" 8 | 9 | "github.com/purkhusid/biff/query" 10 | ) 11 | 12 | type LabelOutput struct { 13 | Hash string `json:"hash"` 14 | Kind string `json:"kind"` 15 | Tags []string `json:"tags"` 16 | } 17 | 18 | func WriteResultsToFile(hashedRuleTargets []HashedRuleTarget, path string) { 19 | f, err := os.Create(path) 20 | if err != nil { 21 | f.Close() 22 | log.Panic(err) 23 | return 24 | } 25 | 26 | output := make(map[string]LabelOutput, 0) 27 | for _, ruleTarget := range hashedRuleTargets { 28 | output[ruleTarget.Rule.GetName()] = LabelOutput{ 29 | Hash: ruleTarget.Hash, 30 | Kind: ruleTarget.Rule.GetRuleClass(), 31 | Tags: query.GetTags(ruleTarget.Rule), 32 | } 33 | } 34 | 35 | bytes, err := json.MarshalIndent(output, "", " ") 36 | 37 | if err != nil { 38 | log.Panic(err) 39 | } 40 | 41 | _, err = f.Write(bytes) 42 | if err != nil { 43 | log.Panic(err) 44 | } 45 | 46 | err = f.Close() 47 | if err != nil { 48 | log.Panic(err) 49 | } 50 | fmt.Println("file written successfully") 51 | } 52 | -------------------------------------------------------------------------------- /calculate/query.go: -------------------------------------------------------------------------------- 1 | package calculate 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "log" 7 | "os" 8 | "os/exec" 9 | 10 | "github.com/golang/protobuf/proto" 11 | blaze_query "github.com/purkhusid/biff/protos" 12 | ) 13 | 14 | func GetQueryResult(workspacePath, bazel, query string) *blaze_query.QueryResult { 15 | ctx, _ := context.WithCancel(context.Background()) 16 | args := append([]string(nil), "query", "--output=proto", "--order_output=full", query) 17 | stdoutBuffer := new(bytes.Buffer) 18 | stdErrBuffer := new(bytes.Buffer) 19 | command := exec.CommandContext(ctx, bazel, args...) 20 | command.Dir = workspacePath 21 | command.Stdout = stdoutBuffer 22 | command.Stderr = stdErrBuffer 23 | err := command.Run() 24 | 25 | if err != nil { 26 | log.Println("Failed to run query") 27 | log.Println("Stdout:") 28 | log.Println(string(stdoutBuffer.Bytes())) 29 | log.Println("Stderr:") 30 | log.Println(string(stdErrBuffer.Bytes())) 31 | os.Exit(1) 32 | } 33 | 34 | queryResult := &blaze_query.QueryResult{} 35 | err = proto.Unmarshal(stdoutBuffer.Bytes(), queryResult) 36 | 37 | if err != nil { 38 | log.Panicf("Failed to unmarshal query output: %s", err) 39 | } 40 | 41 | return queryResult 42 | } 43 | -------------------------------------------------------------------------------- /cmd/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "go_default_library", 5 | srcs = [ 6 | "calculate.go", 7 | "compare.go", 8 | "root.go", 9 | ], 10 | importpath = "github.com/purkhusid/biff/cmd", 11 | visibility = ["//visibility:public"], 12 | deps = [ 13 | "//calculate:go_default_library", 14 | "//compare:go_default_library", 15 | "@com_github_spf13_cobra//:go_default_library", 16 | ], 17 | ) 18 | -------------------------------------------------------------------------------- /cmd/calculate.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "path" 5 | 6 | "github.com/purkhusid/biff/calculate" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | var ( 11 | query string 12 | bazelFlag string 13 | workspacePathFlag string 14 | calculateOutputPathFlag string 15 | ) 16 | 17 | func init() { 18 | rootCmd.AddCommand(calculateCmd) 19 | calculateCmd.Flags().StringVar(&query, "query", "//external:all-targets + deps(//...:all-targets)", "The query used to query the bazel target graph") 20 | calculateCmd.Flags().StringVar(&bazelFlag, "bazel", "", "Location of Bazel executable. By default uses bazel from path") 21 | calculateCmd.Flags().StringVar(&workspacePathFlag, "workspace", "", "Path to the workspace root (Required)") 22 | calculateCmd.Flags().StringVar(&calculateOutputPathFlag, "out", "", "Where the output should be written to (Required)") 23 | calculateCmd.MarkFlagRequired("workspace") 24 | calculateCmd.MarkFlagRequired("out") 25 | } 26 | 27 | var calculateCmd = &cobra.Command{ 28 | Use: "calculate", 29 | Short: "Calculate the checksum for each target in the Bazel graph.", 30 | Long: `Calculates the sha256 checksum for each target in the graph and outputs it to a file. 31 | This file can then be diffed against another output with the compare command`, 32 | Run: func(cmd *cobra.Command, args []string) { 33 | bazel := bazelFlag 34 | workspacePath := workspacePathFlag 35 | calculateOutputPathFlag := calculateOutputPathFlag 36 | 37 | queryResult := calculate.GetQueryResult(workspacePath, bazel, query) 38 | calculator := calculate.NewCalculator(queryResult) 39 | hashedTargets := calculator.CalculateHashes() 40 | 41 | calculate.WriteResultsToFile(hashedTargets, path.Join(workspacePath, calculateOutputPathFlag)) 42 | }, 43 | } 44 | -------------------------------------------------------------------------------- /cmd/compare.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/purkhusid/biff/compare" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var ( 9 | leftFlag string 10 | rightFlag string 11 | comparePutputPathFlag string 12 | ) 13 | 14 | func init() { 15 | rootCmd.AddCommand(compareCommand) 16 | compareCommand.Flags().StringVar(&leftFlag, "left", "", "The 'left' side of the comparison (Required)") 17 | compareCommand.Flags().StringVar(&rightFlag, "right", "", "The 'right' side of the comparison (Required)") 18 | compareCommand.Flags().StringVar(&comparePutputPathFlag, "out", "", "Where the output should be written to (Required)") 19 | compareCommand.MarkFlagRequired("left") 20 | compareCommand.MarkFlagRequired("right") 21 | compareCommand.MarkFlagRequired("out") 22 | } 23 | 24 | var compareCommand = &cobra.Command{ 25 | Use: "compare", 26 | Short: "Compares two outputs from the calculate command", 27 | Long: `Compares two outputs from the calculate command and outputs on the same format as the inputs with only the changed targets`, 28 | Run: func(cmd *cobra.Command, args []string) { 29 | compare.CompareAndWriteToOutput(leftFlag, rightFlag, comparePutputPathFlag) 30 | }, 31 | } 32 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | var rootCmd = &cobra.Command{ 11 | Use: "biff", 12 | Short: "Biff is a tool for comparing Bazel dependency graphs", 13 | } 14 | 15 | // Execute ... 16 | func Execute() { 17 | if err := rootCmd.Execute(); err != nil { 18 | fmt.Println(err) 19 | os.Exit(1) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /compare/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "go_default_library", 5 | srcs = ["compare.go"], 6 | importpath = "github.com/purkhusid/biff/compare", 7 | visibility = ["//visibility:public"], 8 | deps = ["//calculate:go_default_library"], 9 | ) 10 | -------------------------------------------------------------------------------- /compare/compare.go: -------------------------------------------------------------------------------- 1 | package compare 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "log" 8 | "os" 9 | 10 | "github.com/purkhusid/biff/calculate" 11 | ) 12 | 13 | func readFileToJSON(path string) map[string]calculate.LabelOutput { 14 | file, err := os.Open(path) 15 | 16 | if err != nil { 17 | log.Panic(err) 18 | } 19 | 20 | bytes, err := ioutil.ReadAll(file) 21 | 22 | if err != nil { 23 | log.Panic(err) 24 | } 25 | 26 | var output map[string]calculate.LabelOutput 27 | err = json.Unmarshal(bytes, &output) 28 | 29 | if err != nil { 30 | log.Panic(err) 31 | } 32 | 33 | return output 34 | } 35 | 36 | func compare(left map[string]calculate.LabelOutput, right map[string]calculate.LabelOutput) map[string]calculate.LabelOutput { 37 | changedLabels := make(map[string]calculate.LabelOutput) 38 | for k, v := range right { 39 | leftValue, exists := left[k] 40 | 41 | if exists { 42 | if leftValue.Hash != v.Hash { 43 | changedLabels[k] = v 44 | } 45 | } else { 46 | changedLabels[k] = v 47 | } 48 | 49 | } 50 | 51 | return changedLabels 52 | } 53 | 54 | //CompareAndWriteToOutput ... 55 | func CompareAndWriteToOutput(leftPath string, rightPath string, outputPath string) { 56 | leftOutput := readFileToJSON(leftPath) 57 | rightOutput := readFileToJSON(rightPath) 58 | 59 | changedLabels := compare(leftOutput, rightOutput) 60 | 61 | f, err := os.Create(outputPath) 62 | if err != nil { 63 | f.Close() 64 | log.Panic(err) 65 | return 66 | } 67 | 68 | bytes, err := json.MarshalIndent(changedLabels, "", " ") 69 | 70 | _, err = f.Write(bytes) 71 | if err != nil { 72 | log.Panic(err) 73 | } 74 | 75 | err = f.Close() 76 | if err != nil { 77 | log.Panic(err) 78 | } 79 | fmt.Println("file written successfully") 80 | } 81 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/purkhusid/biff 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/golang/protobuf v1.3.1 7 | github.com/spf13/cobra v1.0.0 8 | ) 9 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 3 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 4 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 5 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 6 | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= 7 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 8 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 9 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 10 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 11 | github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= 12 | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 13 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 14 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 15 | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 16 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 17 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 18 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 19 | github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= 20 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 21 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 22 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 23 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 24 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 25 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 26 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 27 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 28 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 29 | github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 30 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 31 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 32 | github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= 33 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 34 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 35 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 36 | github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 37 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 38 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 39 | github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 40 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 41 | github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= 42 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 43 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 44 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 45 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 46 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 47 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 48 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 49 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 50 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 51 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 52 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 53 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 54 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 55 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 56 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 57 | github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= 58 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 59 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 60 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 61 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 62 | github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= 63 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 64 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 65 | github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= 66 | github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 67 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 68 | github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 69 | github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= 70 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 71 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 72 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 73 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 74 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= 75 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 76 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 77 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 78 | github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= 79 | github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= 80 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 81 | github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= 82 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 83 | github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= 84 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 85 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 86 | github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 87 | github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= 88 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 89 | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= 90 | go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 91 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 92 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 93 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 94 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 95 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 96 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 97 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 98 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 99 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 100 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 101 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 102 | golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 103 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 104 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 105 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 106 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 107 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 108 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 109 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 110 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 111 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 112 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 113 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 114 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 115 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 116 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 117 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 118 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 119 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 120 | google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 121 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 122 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 123 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 124 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 125 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 126 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 127 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 128 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 129 | -------------------------------------------------------------------------------- /godeps_macro.bzl: -------------------------------------------------------------------------------- 1 | load("@bazel_gazelle//:deps.bzl", "go_repository") 2 | 3 | def go_repositories(): 4 | go_repository( 5 | name = "co_honnef_go_tools", 6 | importpath = "honnef.co/go/tools", 7 | sum = "h1:XJP7lxbSxWLOMNdBE4B/STaqVy6L73o0knwj2vIlxnw=", 8 | version = "v0.0.0-20190102054323-c2f93a96b099", 9 | ) 10 | go_repository( 11 | name = "com_github_alecthomas_template", 12 | importpath = "github.com/alecthomas/template", 13 | sum = "h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU=", 14 | version = "v0.0.0-20160405071501-a0175ee3bccc", 15 | ) 16 | go_repository( 17 | name = "com_github_alecthomas_units", 18 | importpath = "github.com/alecthomas/units", 19 | sum = "h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=", 20 | version = "v0.0.0-20151022065526-2efee857e7cf", 21 | ) 22 | go_repository( 23 | name = "com_github_armon_consul_api", 24 | importpath = "github.com/armon/consul-api", 25 | sum = "h1:G1bPvciwNyF7IUmKXNt9Ak3m6u9DE1rF+RmtIkBpVdA=", 26 | version = "v0.0.0-20180202201655-eb2c6b5be1b6", 27 | ) 28 | go_repository( 29 | name = "com_github_beorn7_perks", 30 | importpath = "github.com/beorn7/perks", 31 | sum = "h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=", 32 | version = "v1.0.0", 33 | ) 34 | go_repository( 35 | name = "com_github_burntsushi_toml", 36 | importpath = "github.com/BurntSushi/toml", 37 | sum = "h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=", 38 | version = "v0.3.1", 39 | ) 40 | go_repository( 41 | name = "com_github_cespare_xxhash", 42 | importpath = "github.com/cespare/xxhash", 43 | sum = "h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=", 44 | version = "v1.1.0", 45 | ) 46 | go_repository( 47 | name = "com_github_client9_misspell", 48 | importpath = "github.com/client9/misspell", 49 | sum = "h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=", 50 | version = "v0.3.4", 51 | ) 52 | go_repository( 53 | name = "com_github_coreos_bbolt", 54 | importpath = "github.com/coreos/bbolt", 55 | sum = "h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s=", 56 | version = "v1.3.2", 57 | ) 58 | go_repository( 59 | name = "com_github_coreos_etcd", 60 | importpath = "github.com/coreos/etcd", 61 | sum = "h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04=", 62 | version = "v3.3.10+incompatible", 63 | ) 64 | go_repository( 65 | name = "com_github_coreos_go_semver", 66 | importpath = "github.com/coreos/go-semver", 67 | sum = "h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY=", 68 | version = "v0.2.0", 69 | ) 70 | go_repository( 71 | name = "com_github_coreos_go_systemd", 72 | importpath = "github.com/coreos/go-systemd", 73 | sum = "h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=", 74 | version = "v0.0.0-20190321100706-95778dfbb74e", 75 | ) 76 | go_repository( 77 | name = "com_github_coreos_pkg", 78 | importpath = "github.com/coreos/pkg", 79 | sum = "h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=", 80 | version = "v0.0.0-20180928190104-399ea9e2e55f", 81 | ) 82 | go_repository( 83 | name = "com_github_cpuguy83_go_md2man_v2", 84 | importpath = "github.com/cpuguy83/go-md2man/v2", 85 | sum = "h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=", 86 | version = "v2.0.0", 87 | ) 88 | go_repository( 89 | name = "com_github_davecgh_go_spew", 90 | importpath = "github.com/davecgh/go-spew", 91 | sum = "h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=", 92 | version = "v1.1.1", 93 | ) 94 | go_repository( 95 | name = "com_github_dgrijalva_jwt_go", 96 | importpath = "github.com/dgrijalva/jwt-go", 97 | sum = "h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=", 98 | version = "v3.2.0+incompatible", 99 | ) 100 | go_repository( 101 | name = "com_github_dgryski_go_sip13", 102 | importpath = "github.com/dgryski/go-sip13", 103 | sum = "h1:RMLoZVzv4GliuWafOuPuQDKSm1SJph7uCRnnS61JAn4=", 104 | version = "v0.0.0-20181026042036-e10d5fee7954", 105 | ) 106 | go_repository( 107 | name = "com_github_fsnotify_fsnotify", 108 | importpath = "github.com/fsnotify/fsnotify", 109 | sum = "h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=", 110 | version = "v1.4.7", 111 | ) 112 | go_repository( 113 | name = "com_github_ghodss_yaml", 114 | importpath = "github.com/ghodss/yaml", 115 | sum = "h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=", 116 | version = "v1.0.0", 117 | ) 118 | go_repository( 119 | name = "com_github_go_kit_kit", 120 | importpath = "github.com/go-kit/kit", 121 | sum = "h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0=", 122 | version = "v0.8.0", 123 | ) 124 | go_repository( 125 | name = "com_github_go_logfmt_logfmt", 126 | importpath = "github.com/go-logfmt/logfmt", 127 | sum = "h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA=", 128 | version = "v0.4.0", 129 | ) 130 | go_repository( 131 | name = "com_github_go_stack_stack", 132 | importpath = "github.com/go-stack/stack", 133 | sum = "h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=", 134 | version = "v1.8.0", 135 | ) 136 | go_repository( 137 | name = "com_github_gogo_protobuf", 138 | importpath = "github.com/gogo/protobuf", 139 | sum = "h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=", 140 | version = "v1.2.1", 141 | ) 142 | go_repository( 143 | name = "com_github_golang_glog", 144 | importpath = "github.com/golang/glog", 145 | sum = "h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=", 146 | version = "v0.0.0-20160126235308-23def4e6c14b", 147 | ) 148 | go_repository( 149 | name = "com_github_golang_groupcache", 150 | importpath = "github.com/golang/groupcache", 151 | sum = "h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk=", 152 | version = "v0.0.0-20190129154638-5b532d6fd5ef", 153 | ) 154 | go_repository( 155 | name = "com_github_golang_mock", 156 | importpath = "github.com/golang/mock", 157 | sum = "h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8=", 158 | version = "v1.1.1", 159 | ) 160 | go_repository( 161 | name = "com_github_golang_protobuf", 162 | importpath = "github.com/golang/protobuf", 163 | sum = "h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=", 164 | version = "v1.3.1", 165 | ) 166 | go_repository( 167 | name = "com_github_google_btree", 168 | importpath = "github.com/google/btree", 169 | sum = "h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=", 170 | version = "v1.0.0", 171 | ) 172 | go_repository( 173 | name = "com_github_google_go_cmp", 174 | importpath = "github.com/google/go-cmp", 175 | sum = "h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=", 176 | version = "v0.2.0", 177 | ) 178 | go_repository( 179 | name = "com_github_gorilla_websocket", 180 | importpath = "github.com/gorilla/websocket", 181 | sum = "h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=", 182 | version = "v1.4.0", 183 | ) 184 | go_repository( 185 | name = "com_github_grpc_ecosystem_go_grpc_middleware", 186 | importpath = "github.com/grpc-ecosystem/go-grpc-middleware", 187 | sum = "h1:Iju5GlWwrvL6UBg4zJJt3btmonfrMlCDdsejg4CZE7c=", 188 | version = "v1.0.0", 189 | ) 190 | go_repository( 191 | name = "com_github_grpc_ecosystem_go_grpc_prometheus", 192 | importpath = "github.com/grpc-ecosystem/go-grpc-prometheus", 193 | sum = "h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=", 194 | version = "v1.2.0", 195 | ) 196 | go_repository( 197 | name = "com_github_grpc_ecosystem_grpc_gateway", 198 | importpath = "github.com/grpc-ecosystem/grpc-gateway", 199 | sum = "h1:bM6ZAFZmc/wPFaRDi0d5L7hGEZEx/2u+Tmr2evNHDiI=", 200 | version = "v1.9.0", 201 | ) 202 | go_repository( 203 | name = "com_github_hashicorp_hcl", 204 | importpath = "github.com/hashicorp/hcl", 205 | sum = "h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=", 206 | version = "v1.0.0", 207 | ) 208 | go_repository( 209 | name = "com_github_inconshreveable_mousetrap", 210 | importpath = "github.com/inconshreveable/mousetrap", 211 | sum = "h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=", 212 | version = "v1.0.0", 213 | ) 214 | go_repository( 215 | name = "com_github_jonboulle_clockwork", 216 | importpath = "github.com/jonboulle/clockwork", 217 | sum = "h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=", 218 | version = "v0.1.0", 219 | ) 220 | go_repository( 221 | name = "com_github_julienschmidt_httprouter", 222 | importpath = "github.com/julienschmidt/httprouter", 223 | sum = "h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g=", 224 | version = "v1.2.0", 225 | ) 226 | go_repository( 227 | name = "com_github_kisielk_errcheck", 228 | importpath = "github.com/kisielk/errcheck", 229 | sum = "h1:ZqfnKyx9KGpRcW04j5nnPDgRgoXUeLh2YFBeFzphcA0=", 230 | version = "v1.1.0", 231 | ) 232 | go_repository( 233 | name = "com_github_kisielk_gotool", 234 | importpath = "github.com/kisielk/gotool", 235 | sum = "h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=", 236 | version = "v1.0.0", 237 | ) 238 | go_repository( 239 | name = "com_github_konsorten_go_windows_terminal_sequences", 240 | importpath = "github.com/konsorten/go-windows-terminal-sequences", 241 | sum = "h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=", 242 | version = "v1.0.1", 243 | ) 244 | go_repository( 245 | name = "com_github_kr_logfmt", 246 | importpath = "github.com/kr/logfmt", 247 | sum = "h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=", 248 | version = "v0.0.0-20140226030751-b84e30acd515", 249 | ) 250 | go_repository( 251 | name = "com_github_kr_pretty", 252 | importpath = "github.com/kr/pretty", 253 | sum = "h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=", 254 | version = "v0.1.0", 255 | ) 256 | go_repository( 257 | name = "com_github_kr_pty", 258 | importpath = "github.com/kr/pty", 259 | sum = "h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw=", 260 | version = "v1.1.1", 261 | ) 262 | go_repository( 263 | name = "com_github_kr_text", 264 | importpath = "github.com/kr/text", 265 | sum = "h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=", 266 | version = "v0.1.0", 267 | ) 268 | go_repository( 269 | name = "com_github_magiconair_properties", 270 | importpath = "github.com/magiconair/properties", 271 | sum = "h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=", 272 | version = "v1.8.0", 273 | ) 274 | go_repository( 275 | name = "com_github_matttproud_golang_protobuf_extensions", 276 | importpath = "github.com/matttproud/golang_protobuf_extensions", 277 | sum = "h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=", 278 | version = "v1.0.1", 279 | ) 280 | go_repository( 281 | name = "com_github_mitchellh_go_homedir", 282 | importpath = "github.com/mitchellh/go-homedir", 283 | sum = "h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=", 284 | version = "v1.1.0", 285 | ) 286 | go_repository( 287 | name = "com_github_mitchellh_mapstructure", 288 | importpath = "github.com/mitchellh/mapstructure", 289 | sum = "h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=", 290 | version = "v1.1.2", 291 | ) 292 | go_repository( 293 | name = "com_github_mwitkow_go_conntrack", 294 | importpath = "github.com/mwitkow/go-conntrack", 295 | sum = "h1:F9x/1yl3T2AeKLr2AMdilSD8+f9bvMnNN8VS5iDtovc=", 296 | version = "v0.0.0-20161129095857-cc309e4a2223", 297 | ) 298 | go_repository( 299 | name = "com_github_oklog_ulid", 300 | importpath = "github.com/oklog/ulid", 301 | sum = "h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=", 302 | version = "v1.3.1", 303 | ) 304 | go_repository( 305 | name = "com_github_oneofone_xxhash", 306 | importpath = "github.com/OneOfOne/xxhash", 307 | sum = "h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=", 308 | version = "v1.2.2", 309 | ) 310 | go_repository( 311 | name = "com_github_pelletier_go_toml", 312 | importpath = "github.com/pelletier/go-toml", 313 | sum = "h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=", 314 | version = "v1.2.0", 315 | ) 316 | go_repository( 317 | name = "com_github_pkg_errors", 318 | importpath = "github.com/pkg/errors", 319 | sum = "h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=", 320 | version = "v0.8.0", 321 | ) 322 | go_repository( 323 | name = "com_github_pmezard_go_difflib", 324 | importpath = "github.com/pmezard/go-difflib", 325 | sum = "h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=", 326 | version = "v1.0.0", 327 | ) 328 | go_repository( 329 | name = "com_github_prometheus_client_golang", 330 | importpath = "github.com/prometheus/client_golang", 331 | sum = "h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8=", 332 | version = "v0.9.3", 333 | ) 334 | go_repository( 335 | name = "com_github_prometheus_client_model", 336 | importpath = "github.com/prometheus/client_model", 337 | sum = "h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=", 338 | version = "v0.0.0-20190129233127-fd36f4220a90", 339 | ) 340 | go_repository( 341 | name = "com_github_prometheus_common", 342 | importpath = "github.com/prometheus/common", 343 | sum = "h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM=", 344 | version = "v0.4.0", 345 | ) 346 | go_repository( 347 | name = "com_github_prometheus_procfs", 348 | importpath = "github.com/prometheus/procfs", 349 | sum = "h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY=", 350 | version = "v0.0.0-20190507164030-5867b95ac084", 351 | ) 352 | go_repository( 353 | name = "com_github_prometheus_tsdb", 354 | importpath = "github.com/prometheus/tsdb", 355 | sum = "h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA=", 356 | version = "v0.7.1", 357 | ) 358 | go_repository( 359 | name = "com_github_rogpeppe_fastuuid", 360 | importpath = "github.com/rogpeppe/fastuuid", 361 | sum = "h1:gu+uRPtBe88sKxUCEXRoeCvVG90TJmwhiqRpvdhQFng=", 362 | version = "v0.0.0-20150106093220-6724a57986af", 363 | ) 364 | go_repository( 365 | name = "com_github_russross_blackfriday_v2", 366 | importpath = "github.com/russross/blackfriday/v2", 367 | sum = "h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=", 368 | version = "v2.0.1", 369 | ) 370 | go_repository( 371 | name = "com_github_shurcool_sanitized_anchor_name", 372 | importpath = "github.com/shurcooL/sanitized_anchor_name", 373 | sum = "h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=", 374 | version = "v1.0.0", 375 | ) 376 | go_repository( 377 | name = "com_github_sirupsen_logrus", 378 | importpath = "github.com/sirupsen/logrus", 379 | sum = "h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=", 380 | version = "v1.2.0", 381 | ) 382 | go_repository( 383 | name = "com_github_soheilhy_cmux", 384 | importpath = "github.com/soheilhy/cmux", 385 | sum = "h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E=", 386 | version = "v0.1.4", 387 | ) 388 | go_repository( 389 | name = "com_github_spaolacci_murmur3", 390 | importpath = "github.com/spaolacci/murmur3", 391 | sum = "h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=", 392 | version = "v0.0.0-20180118202830-f09979ecbc72", 393 | ) 394 | go_repository( 395 | name = "com_github_spf13_afero", 396 | importpath = "github.com/spf13/afero", 397 | sum = "h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=", 398 | version = "v1.1.2", 399 | ) 400 | go_repository( 401 | name = "com_github_spf13_cast", 402 | importpath = "github.com/spf13/cast", 403 | sum = "h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=", 404 | version = "v1.3.0", 405 | ) 406 | go_repository( 407 | name = "com_github_spf13_cobra", 408 | importpath = "github.com/spf13/cobra", 409 | sum = "h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=", 410 | version = "v1.0.0", 411 | ) 412 | go_repository( 413 | name = "com_github_spf13_jwalterweatherman", 414 | importpath = "github.com/spf13/jwalterweatherman", 415 | sum = "h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=", 416 | version = "v1.0.0", 417 | ) 418 | go_repository( 419 | name = "com_github_spf13_pflag", 420 | importpath = "github.com/spf13/pflag", 421 | sum = "h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=", 422 | version = "v1.0.3", 423 | ) 424 | go_repository( 425 | name = "com_github_spf13_viper", 426 | importpath = "github.com/spf13/viper", 427 | sum = "h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=", 428 | version = "v1.4.0", 429 | ) 430 | go_repository( 431 | name = "com_github_stretchr_objx", 432 | importpath = "github.com/stretchr/objx", 433 | sum = "h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=", 434 | version = "v0.1.1", 435 | ) 436 | go_repository( 437 | name = "com_github_stretchr_testify", 438 | importpath = "github.com/stretchr/testify", 439 | sum = "h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=", 440 | version = "v1.2.2", 441 | ) 442 | go_repository( 443 | name = "com_github_tmc_grpc_websocket_proxy", 444 | importpath = "github.com/tmc/grpc-websocket-proxy", 445 | sum = "h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ=", 446 | version = "v0.0.0-20190109142713-0ad062ec5ee5", 447 | ) 448 | go_repository( 449 | name = "com_github_ugorji_go", 450 | importpath = "github.com/ugorji/go", 451 | sum = "h1:j4s+tAvLfL3bZyefP2SEWmhBzmuIlH/eqNuPdFPgngw=", 452 | version = "v1.1.4", 453 | ) 454 | go_repository( 455 | name = "com_github_xiang90_probing", 456 | importpath = "github.com/xiang90/probing", 457 | sum = "h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=", 458 | version = "v0.0.0-20190116061207-43a291ad63a2", 459 | ) 460 | go_repository( 461 | name = "com_github_xordataexchange_crypt", 462 | importpath = "github.com/xordataexchange/crypt", 463 | sum = "h1:ESFSdwYZvkeru3RtdrYueztKhOBCSAAzS4Gf+k0tEow=", 464 | version = "v0.0.3-0.20170626215501-b2862e3d0a77", 465 | ) 466 | go_repository( 467 | name = "com_google_cloud_go", 468 | importpath = "cloud.google.com/go", 469 | sum = "h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ=", 470 | version = "v0.26.0", 471 | ) 472 | go_repository( 473 | name = "in_gopkg_alecthomas_kingpin_v2", 474 | importpath = "gopkg.in/alecthomas/kingpin.v2", 475 | sum = "h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=", 476 | version = "v2.2.6", 477 | ) 478 | go_repository( 479 | name = "in_gopkg_check_v1", 480 | importpath = "gopkg.in/check.v1", 481 | sum = "h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=", 482 | version = "v1.0.0-20180628173108-788fd7840127", 483 | ) 484 | go_repository( 485 | name = "in_gopkg_resty_v1", 486 | importpath = "gopkg.in/resty.v1", 487 | sum = "h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI=", 488 | version = "v1.12.0", 489 | ) 490 | go_repository( 491 | name = "in_gopkg_yaml_v2", 492 | importpath = "gopkg.in/yaml.v2", 493 | sum = "h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=", 494 | version = "v2.2.2", 495 | ) 496 | go_repository( 497 | name = "io_etcd_go_bbolt", 498 | importpath = "go.etcd.io/bbolt", 499 | sum = "h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk=", 500 | version = "v1.3.2", 501 | ) 502 | go_repository( 503 | name = "org_golang_google_appengine", 504 | importpath = "google.golang.org/appengine", 505 | sum = "h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs=", 506 | version = "v1.1.0", 507 | ) 508 | go_repository( 509 | name = "org_golang_google_genproto", 510 | importpath = "google.golang.org/genproto", 511 | sum = "h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=", 512 | version = "v0.0.0-20180817151627-c66870c02cf8", 513 | ) 514 | go_repository( 515 | name = "org_golang_google_grpc", 516 | importpath = "google.golang.org/grpc", 517 | sum = "h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0=", 518 | version = "v1.21.0", 519 | ) 520 | go_repository( 521 | name = "org_golang_x_crypto", 522 | importpath = "golang.org/x/crypto", 523 | sum = "h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=", 524 | version = "v0.0.0-20190308221718-c2843e01d9a2", 525 | ) 526 | go_repository( 527 | name = "org_golang_x_lint", 528 | importpath = "golang.org/x/lint", 529 | sum = "h1:XQyxROzUlZH+WIQwySDgnISgOivlhjIEwaQaJEJrrN0=", 530 | version = "v0.0.0-20190313153728-d0100b6bd8b3", 531 | ) 532 | go_repository( 533 | name = "org_golang_x_net", 534 | importpath = "golang.org/x/net", 535 | sum = "h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco=", 536 | version = "v0.0.0-20190522155817-f3200d17e092", 537 | ) 538 | go_repository( 539 | name = "org_golang_x_oauth2", 540 | importpath = "golang.org/x/oauth2", 541 | sum = "h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=", 542 | version = "v0.0.0-20180821212333-d2e6202438be", 543 | ) 544 | go_repository( 545 | name = "org_golang_x_sync", 546 | importpath = "golang.org/x/sync", 547 | sum = "h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=", 548 | version = "v0.0.0-20181221193216-37e7f081c4d4", 549 | ) 550 | go_repository( 551 | name = "org_golang_x_sys", 552 | importpath = "golang.org/x/sys", 553 | sum = "h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=", 554 | version = "v0.0.0-20190215142949-d0b11bdaac8a", 555 | ) 556 | go_repository( 557 | name = "org_golang_x_text", 558 | importpath = "golang.org/x/text", 559 | sum = "h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=", 560 | version = "v0.3.0", 561 | ) 562 | go_repository( 563 | name = "org_golang_x_time", 564 | importpath = "golang.org/x/time", 565 | sum = "h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=", 566 | version = "v0.0.0-20190308202827-9d24e82272b4", 567 | ) 568 | go_repository( 569 | name = "org_golang_x_tools", 570 | importpath = "golang.org/x/tools", 571 | sum = "h1:/e+gpKk9r3dJobndpTytxS2gOy6m5uvpg+ISQoEcusQ=", 572 | version = "v0.0.0-20190311212946-11955173bddd", 573 | ) 574 | go_repository( 575 | name = "org_uber_go_atomic", 576 | importpath = "go.uber.org/atomic", 577 | sum = "h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=", 578 | version = "v1.4.0", 579 | ) 580 | go_repository( 581 | name = "org_uber_go_multierr", 582 | importpath = "go.uber.org/multierr", 583 | sum = "h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=", 584 | version = "v1.1.0", 585 | ) 586 | go_repository( 587 | name = "org_uber_go_zap", 588 | importpath = "go.uber.org/zap", 589 | sum = "h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=", 590 | version = "v1.10.0", 591 | ) 592 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/purkhusid/biff/cmd" 5 | ) 6 | 7 | func main() { 8 | cmd.Execute() 9 | } 10 | -------------------------------------------------------------------------------- /protos/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@rules_proto//proto:defs.bzl", "proto_library") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 3 | load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") 4 | 5 | proto_library( 6 | name = "blaze_query_proto", 7 | srcs = ["query.proto"], 8 | visibility = ["//visibility:public"], 9 | ) 10 | 11 | go_proto_library( 12 | name = "blaze_query_go_proto", 13 | importpath = "github.com/purkhusid/biff/protos", 14 | proto = ":blaze_query_proto", 15 | visibility = ["//visibility:public"], 16 | ) 17 | 18 | go_library( 19 | name = "go_default_library", 20 | embed = [":blaze_query_go_proto"], 21 | importpath = "github.com/purkhusid/biff/protos", 22 | visibility = ["//visibility:public"], 23 | ) 24 | -------------------------------------------------------------------------------- /protos/query.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Bazel Authors. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This file contains the protocol buffer representation of a build 16 | // file or 'blaze query --output=proto' call. 17 | 18 | syntax = "proto2"; 19 | 20 | package blaze_query; 21 | 22 | // option cc_api_version = 2; 23 | // option java_api_version = 1; 24 | 25 | option java_package = "com.google.devtools.build.lib.query2.proto.proto2api"; 26 | 27 | message License { 28 | repeated string license_type = 1; 29 | repeated string exception = 2; 30 | } 31 | 32 | message StringDictEntry { 33 | required string key = 1; 34 | required string value = 2; 35 | } 36 | 37 | message LabelDictUnaryEntry { 38 | required string key = 1; 39 | required string value = 2; 40 | } 41 | 42 | message LabelListDictEntry { 43 | required string key = 1; 44 | repeated string value = 2; 45 | } 46 | 47 | message LabelKeyedStringDictEntry { 48 | required string key = 1; 49 | required string value = 2; 50 | } 51 | 52 | message StringListDictEntry { 53 | required string key = 1; 54 | repeated string value = 2; 55 | } 56 | 57 | // Represents an entry attribute of a Fileset rule in a build file. 58 | message FilesetEntry { 59 | // Indicates what to do when a source file is actually a symlink. 60 | enum SymlinkBehavior { 61 | COPY = 1; 62 | DEREFERENCE = 2; 63 | } 64 | 65 | // The label pointing to the source target where files are copied from. 66 | required string source = 1; 67 | 68 | // The relative path within the fileset rule where files will be mapped. 69 | required string destination_directory = 2; 70 | 71 | // Whether the files= attribute was specified. This is necessary because 72 | // no files= attribute and files=[] mean different things. 73 | optional bool files_present = 7; 74 | 75 | // A list of file labels to include from the source directory. 76 | repeated string file = 3; 77 | 78 | // If this is a fileset entry representing files within the rule 79 | // package, this lists relative paths to files that should be excluded from 80 | // the set. This cannot contain values if 'file' also has values. 81 | repeated string exclude = 4; 82 | 83 | // This field is optional because there will be some time when the new 84 | // PB is used by tools depending on blaze query, but the new blaze version 85 | // is not yet released. 86 | // TODO(bazel-team): Make this field required once a version of Blaze is 87 | // released that outputs this field. 88 | optional SymlinkBehavior symlink_behavior = 5 [ default=COPY ]; 89 | 90 | // The prefix to strip from the path of the files in this FilesetEntry. Note 91 | // that no value and the empty string as the value mean different things here. 92 | optional string strip_prefix = 6; 93 | } 94 | 95 | // A rule attribute. Each attribute must have a type and one of the various 96 | // value fields populated - for the most part. 97 | // 98 | // Attributes of BOOLEAN and TRISTATE type may set all of the int, bool, and 99 | // string values for backwards compatibility with clients that expect them to 100 | // be set. 101 | // 102 | // Attributes of INTEGER, STRING, LABEL, LICENSE, BOOLEAN, and TRISTATE type 103 | // may set *none* of the values. This can happen if the Attribute message is 104 | // prepared for a client that doesn't support SELECTOR_LIST, but the rule has 105 | // a selector list value for the attribute. (Selector lists for attributes of 106 | // other types--the collection types--are handled differently when prepared 107 | // for such a client. The possible collection values are gathered together 108 | // and flattened.) 109 | // 110 | // By checking the type, the appropriate value can be extracted - see the 111 | // comments on each type for the associated value. The order of lists comes 112 | // from the blaze parsing. If an attribute is of a list type, the associated 113 | // list should never be empty. 114 | message Attribute { 115 | reserved 12, 16; 116 | 117 | // Indicates the type of attribute. 118 | enum Discriminator { 119 | INTEGER = 1; // int_value 120 | STRING = 2; // string_value 121 | LABEL = 3; // string_value 122 | OUTPUT = 4; // string_value 123 | STRING_LIST = 5; // string_list_value 124 | LABEL_LIST = 6; // string_list_value 125 | OUTPUT_LIST = 7; // string_list_value 126 | DISTRIBUTION_SET = 8; // string_list_value - order is unimportant 127 | LICENSE = 9; // license 128 | STRING_DICT = 10; // string_dict_value 129 | FILESET_ENTRY_LIST = 11; // fileset_list_value 130 | LABEL_LIST_DICT = 12; // label_list_dict_value 131 | STRING_LIST_DICT = 13; // string_list_dict_value 132 | BOOLEAN = 14; // int, bool and string value 133 | TRISTATE = 15; // tristate, int and string value 134 | INTEGER_LIST = 16; // int_list_value 135 | UNKNOWN = 18; // unknown type, use only for build extensions 136 | LABEL_DICT_UNARY = 19; // label_dict_unary_value 137 | SELECTOR_LIST = 20; // selector_list 138 | LABEL_KEYED_STRING_DICT = 21; // label_keyed_string_dict 139 | 140 | DEPRECATED_STRING_DICT_UNARY = 17; 141 | 142 | } 143 | 144 | // Values for the TriState field type. 145 | enum Tristate { 146 | NO = 0; 147 | YES = 1; 148 | AUTO = 2; 149 | } 150 | 151 | message SelectorEntry { 152 | reserved 12; 153 | 154 | // The key of the selector entry. At this time, this is the label of a 155 | // config_setting rule, or the pseudo-label "//conditions:default". 156 | optional string label = 1; 157 | 158 | // True if the entry's value is the default value for the type as a 159 | // result of the condition value being specified as None (ie: 160 | // {"//condition": None}). 161 | optional bool is_default_value = 16; 162 | 163 | // Exactly one of the following fields (except for glob_criteria) must be 164 | // populated - note that the BOOLEAN and TRISTATE caveat in Attribute's 165 | // comment does not apply here. The type field in the SelectorList 166 | // containing this entry indicates which of these fields is populated, 167 | // in accordance with the comments on Discriminator enum values above. 168 | // (To be explicit: BOOLEAN populates the boolean_value field and TRISTATE 169 | // populates the tristate_value field.) 170 | optional int32 int_value = 2; 171 | optional string string_value = 3; 172 | optional bool boolean_value = 4; 173 | optional Tristate tristate_value = 5; 174 | repeated string string_list_value = 6; 175 | optional License license = 7; 176 | repeated StringDictEntry string_dict_value = 8; 177 | repeated FilesetEntry fileset_list_value = 9; 178 | repeated LabelListDictEntry label_list_dict_value = 10; 179 | repeated StringListDictEntry string_list_dict_value = 11; 180 | repeated int32 int_list_value = 13; 181 | repeated LabelDictUnaryEntry label_dict_unary_value = 15; 182 | repeated LabelKeyedStringDictEntry label_keyed_string_dict_value = 17; 183 | 184 | repeated bytes DEPRECATED_string_dict_unary_value = 14; 185 | } 186 | 187 | message Selector { 188 | // The list of (label, value) pairs in the map that defines the selector. 189 | // At this time, this cannot be empty, i.e. a selector has at least one 190 | // entry. 191 | repeated SelectorEntry entries = 1; 192 | 193 | // Whether or not this has any default values. 194 | optional bool has_default_value = 2; 195 | 196 | // The error message when no condition matches. 197 | optional string no_match_error = 3; 198 | } 199 | 200 | message SelectorList { 201 | // The type that this selector list evaluates to, and the type that each 202 | // selector in the list evaluates to. At this time, this cannot be 203 | // SELECTOR_LIST, i.e. selector lists do not nest. 204 | optional Discriminator type = 1; 205 | 206 | // The list of selector elements in this selector list. At this time, this 207 | // cannot be empty, i.e. a selector list is never empty. 208 | repeated Selector elements = 2; 209 | } 210 | 211 | // The name of the attribute 212 | required string name = 1; 213 | 214 | // Whether the attribute was explicitly specified 215 | optional bool explicitly_specified = 13; 216 | 217 | // If this attribute has a string value or a string list value, then this 218 | // may be set to indicate that the value may be treated as a label that 219 | // isn't a dependency of this attribute's rule. 220 | optional bool nodep = 20; 221 | 222 | // The type of attribute. This message is used for all of the different 223 | // attribute types so the discriminator helps for figuring out what is 224 | // stored in the message. 225 | required Discriminator type = 2; 226 | 227 | // If this attribute has an integer value this will be populated. 228 | // Boolean and TriState also use this field as [0,1] and [-1,0,1] 229 | // for [false, true] and [auto, no, yes] respectively. 230 | optional int32 int_value = 3; 231 | 232 | // If the attribute has a string value this will be populated. Label and 233 | // path attributes use this field as the value even though the type may 234 | // be LABEL or something else other than STRING. 235 | optional string string_value = 5; 236 | 237 | // If the attribute has a boolean value this will be populated. 238 | optional bool boolean_value = 14; 239 | 240 | // If the attribute is a Tristate value, this will be populated. 241 | optional Tristate tristate_value = 15; 242 | 243 | // The value of the attribute has a list of string values (label and path 244 | // note from STRING applies here as well). 245 | repeated string string_list_value = 6; 246 | 247 | // If this is a license attribute, the license information is stored here. 248 | optional License license = 7; 249 | 250 | // If this is a string dict, each entry will be stored here. 251 | repeated StringDictEntry string_dict_value = 8; 252 | 253 | // If the attribute is part of a Fileset, the fileset entries are stored in 254 | // this field. 255 | repeated FilesetEntry fileset_list_value = 9; 256 | 257 | // If this is a label list dict, each entry will be stored here. 258 | repeated LabelListDictEntry label_list_dict_value = 10; 259 | 260 | // If this is a string list dict, each entry will be stored here. 261 | repeated StringListDictEntry string_list_dict_value = 11; 262 | 263 | // The value of the attribute has a list of int32 values 264 | repeated int32 int_list_value = 17; 265 | 266 | // If this is a label dict unary, each entry will be stored here. 267 | repeated LabelDictUnaryEntry label_dict_unary_value = 19; 268 | 269 | // If this is a label-keyed string dict, each entry will be stored here. 270 | repeated LabelKeyedStringDictEntry label_keyed_string_dict_value = 22; 271 | 272 | // If this attribute's value is an expression containing one or more select 273 | // expressions, then its type is SELECTOR_LIST and a SelectorList will be 274 | // stored here. 275 | optional SelectorList selector_list = 21; 276 | 277 | repeated bytes DEPRECATED_string_dict_unary_value = 18; 278 | } 279 | 280 | // A rule instance (e.g., cc_library foo, java_binary bar). 281 | message Rule { 282 | reserved 8, 11; 283 | 284 | // The name of the rule (formatted as an absolute label, e.g. //foo/bar:baz). 285 | required string name = 1; 286 | 287 | // The rule class (e.g., java_library) 288 | required string rule_class = 2; 289 | 290 | // The BUILD file and line number of the location (formatted as 291 | // :) in the rule's package's BUILD file where the 292 | // rule instance was instantiated. The line number will be that of a rule 293 | // invocation or macro call (that in turn invoked a rule). See 294 | // https://docs.bazel.build/versions/master/skylark/macros.html#macro-creation 295 | optional string location = 3; 296 | 297 | // All of the attributes that describe the rule. 298 | repeated Attribute attribute = 4; 299 | 300 | // All of the inputs to the rule (formatted as absolute labels). These are 301 | // predecessors in the dependency graph. 302 | repeated string rule_input = 5; 303 | 304 | // All of the outputs of the rule (formatted as absolute labels). These are 305 | // successors in the dependency graph. 306 | repeated string rule_output = 6; 307 | 308 | // The set of all "features" inherited from the rule's package declaration. 309 | repeated string default_setting = 7; 310 | 311 | // The rule's class's public by default value. 312 | optional bool public_by_default = 9; 313 | 314 | optional bool DEPRECATED_is_skylark = 10; 315 | 316 | // Hash encapsulating the behavior of this Starlark rule. Any change to this 317 | // rule's definition that could change its behavior will be reflected here. 318 | optional string skylark_environment_hash_code = 12; 319 | } 320 | 321 | // Summary of all transitive dependencies of 'rule,' where each dependent 322 | // rule is included only once in the 'dependency' field. Gives complete 323 | // information to analyze the single build target labeled rule.name, 324 | // including optional location of target in BUILD file. 325 | message RuleSummary { 326 | required Rule rule = 1; 327 | repeated Rule dependency = 2; 328 | optional string location = 3; 329 | } 330 | 331 | // A package group. Aside from the name, it contains the list of packages 332 | // present in the group (as specified in the BUILD file). 333 | message PackageGroup { 334 | reserved 4; 335 | 336 | // The name of the package group 337 | required string name = 1; 338 | 339 | // The list of packages as specified in the BUILD file. Currently this is 340 | // only a list of packages, but some time in the future, there might be 341 | // some type of wildcard mechanism. 342 | repeated string contained_package = 2; 343 | 344 | // The list of sub package groups included in this one. 345 | repeated string included_package_group = 3; 346 | } 347 | 348 | // An environment group. 349 | message EnvironmentGroup { 350 | // The name of the environment group. 351 | required string name = 1; 352 | 353 | // The environments that belong to this group (as labels). 354 | repeated string environment = 2; 355 | 356 | // The member environments that rules implicitly support if not otherwise 357 | // specified. 358 | repeated string default = 3; 359 | } 360 | 361 | // A file that is an input into the build system. 362 | // Next-Id: 10 363 | message SourceFile { 364 | reserved 7; 365 | 366 | // The name of the source file (a label). 367 | required string name = 1; 368 | 369 | // The location of the source file. This is a path with line numbers, not 370 | // a label in the build system. 371 | optional string location = 2; 372 | 373 | // Labels of .bzl (Starlark) files that are transitively loaded in this BUILD 374 | // file. This is present only when the SourceFile represents a BUILD file that 375 | // loaded .bzl files. 376 | // TODO(bazel-team): Rename this field. 377 | repeated string subinclude = 3; 378 | 379 | // Labels of package groups that are mentioned in the visibility declaration 380 | // for this source file. 381 | repeated string package_group = 4; 382 | 383 | // Labels mentioned in the visibility declaration (including :__pkg__ and 384 | // //visibility: ones) 385 | repeated string visibility_label = 5; 386 | 387 | // The package-level features enabled for this package. Only present if the 388 | // SourceFile represents a BUILD file. 389 | repeated string feature = 6; 390 | 391 | // License attribute for the file. 392 | optional License license = 8; 393 | 394 | // True if the package contains an error. Only present if the SourceFile 395 | // represents a BUILD file. 396 | optional bool package_contains_errors = 9; 397 | } 398 | 399 | // A file that is the output of a build rule. 400 | message GeneratedFile { 401 | // The name of the generated file (a label). 402 | required string name = 1; 403 | 404 | // The label of the target that generates the file. 405 | required string generating_rule = 2; 406 | 407 | // The path of the output file (not a label). 408 | optional string location = 3; 409 | } 410 | 411 | // A target from a blaze query execution. Similar to the Attribute message, 412 | // the Discriminator is used to determine which field contains information. 413 | // For any given type, only one of these can be populated in a single Target. 414 | message Target { 415 | enum Discriminator { 416 | RULE = 1; 417 | SOURCE_FILE = 2; 418 | GENERATED_FILE = 3; 419 | PACKAGE_GROUP = 4; 420 | ENVIRONMENT_GROUP = 5; 421 | } 422 | 423 | // The type of target contained in the message. 424 | required Discriminator type = 1; 425 | 426 | // If this target represents a rule, the rule is stored here. 427 | optional Rule rule = 2; 428 | 429 | // A file that is not generated by the build system (version controlled 430 | // or created by the test harness). 431 | optional SourceFile source_file = 3; 432 | 433 | // A generated file that is the output of a rule. 434 | optional GeneratedFile generated_file = 4; 435 | 436 | // A package group. 437 | optional PackageGroup package_group = 5; 438 | 439 | // An environment group. 440 | optional EnvironmentGroup environment_group = 6; 441 | } 442 | 443 | // Container for all of the blaze query results. 444 | message QueryResult { 445 | // All of the targets returned by the blaze query. 446 | repeated Target target = 1; 447 | } 448 | 449 | //////////////////////////////////////////////////////////////////////////// 450 | // Messages dealing with querying the BUILD language itself. For now, this is 451 | // quite simplistic: Blaze can only tell the names of the rule classes, their 452 | // attributes with their type. 453 | 454 | // Information about allowed rule classes for a specific attribute of a rule. 455 | message AllowedRuleClassInfo { 456 | enum AllowedRuleClasses { 457 | ANY = 1; // Any rule is allowed to be in this attribute 458 | SPECIFIED = 2; // Only the explicitly listed rules are allowed 459 | } 460 | 461 | required AllowedRuleClasses policy = 1; 462 | 463 | // Rule class names of rules allowed in this attribute, e.g "cc_library", 464 | // "py_binary". Only present if the allowed_rule_classes field is set to 465 | // SPECIFIED. 466 | repeated string allowed_rule_class = 2; 467 | } 468 | 469 | // This message represents a single attribute of a single rule. 470 | // See docs.bazel.build/versions/master/skylark/lib/attr.html. 471 | message AttributeDefinition { 472 | required string name = 1; // e.g. "name", "srcs" 473 | required Attribute.Discriminator type = 2; 474 | optional bool mandatory = 3; 475 | optional AllowedRuleClassInfo allowed_rule_classes = 4; // type=label* 476 | optional string documentation = 5; 477 | optional bool allow_empty = 6; // type=*_list|*_dict 478 | optional bool allow_single_file = 7; // type=label 479 | optional AttributeValue default = 9; // simple (not computed/late-bound) values only 480 | optional bool executable = 10; // type=label 481 | optional bool configurable = 11; 482 | optional bool nodep = 12; // label-valued edge does not establish a dependency 483 | optional bool cfg_is_host = 13; // edge entails a transition to "host" configuration 484 | } 485 | 486 | // An AttributeValue represents the value of an attribute. 487 | // A single field, determined by the attribute type, is populated. 488 | // 489 | // It is used only for AttributeDefinition.default. Attribute and 490 | // SelectorEntry do their own thing for unfortunate historical reasons. 491 | message AttributeValue { 492 | optional int32 int = 1; // type=int|tristate 493 | optional string string = 2; // type=string|label|output 494 | optional bool bool = 3; // type=bool 495 | repeated AttributeValue list = 4; // type=*_list|distrib 496 | repeated DictEntry dict = 5; // type=*_dict 497 | 498 | message DictEntry { 499 | required string key = 1; 500 | required AttributeValue value = 2; 501 | } 502 | } 503 | 504 | message RuleDefinition { 505 | required string name = 1; 506 | // Only contains documented attributes 507 | repeated AttributeDefinition attribute = 2; 508 | optional string documentation = 3; 509 | // Only for build extensions: label to file that defines the extension 510 | optional string label = 4; 511 | } 512 | 513 | message BuildLanguage { 514 | // Only contains documented rule definitions 515 | repeated RuleDefinition rule = 1; 516 | } 517 | -------------------------------------------------------------------------------- /query/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "go_default_library", 5 | srcs = ["utils.go"], 6 | importpath = "github.com/purkhusid/biff/query", 7 | visibility = ["//visibility:public"], 8 | deps = ["//protos:go_default_library"], 9 | ) 10 | -------------------------------------------------------------------------------- /query/utils.go: -------------------------------------------------------------------------------- 1 | package query 2 | 3 | import ( 4 | blaze_query "github.com/purkhusid/biff/protos" 5 | ) 6 | 7 | func GetRuleTargets(queryResult *blaze_query.QueryResult) []*blaze_query.Rule { 8 | rules := make([]*blaze_query.Rule, 0) 9 | 10 | for _, target := range queryResult.GetTarget() { 11 | if target.GetRule() != nil { 12 | rules = append(rules, target.GetRule()) 13 | } 14 | } 15 | 16 | return rules 17 | } 18 | 19 | func GetRuleAttributes(rule *blaze_query.Rule) []*blaze_query.Attribute { 20 | attributes := make([]*blaze_query.Attribute, 0) 21 | for _, attribute := range rule.GetAttribute() { 22 | name := attribute.GetName() 23 | if name != "generator_location" && name != "path" && name != "build_file" { 24 | attributes = append(attributes, attribute) 25 | } 26 | } 27 | 28 | return attributes 29 | } 30 | 31 | func GetTags(rule *blaze_query.Rule) []string { 32 | var tags = []string{} 33 | for _, attribute := range rule.GetAttribute() { 34 | name := attribute.GetName() 35 | if name == "tags" { 36 | if attribute.GetStringListValue() != nil { 37 | tags = attribute.GetStringListValue() 38 | } 39 | } 40 | } 41 | 42 | return tags 43 | } 44 | 45 | func GetTargetLabel(target *blaze_query.Target) string { 46 | if target.GetRule() != nil { 47 | return target.GetRule().GetName() 48 | } 49 | 50 | if target.GetSourceFile() != nil { 51 | return target.GetSourceFile().GetName() 52 | } 53 | 54 | if target.GetGeneratedFile() != nil { 55 | return target.GetGeneratedFile().GetName() 56 | } 57 | 58 | // TODO: Should I include PackageGroup ? 59 | // if target.GetPackageGroup() != nil { 60 | // return target.GetPackageGroup().GetName() 61 | // } 62 | 63 | // TODO: Should I include EnvironmentGroup ? 64 | // if target.GetEnvironmentGroup() != nil { 65 | // return target.GetEnvironmentGroup().GetName() 66 | // } 67 | 68 | return "" 69 | } 70 | --------------------------------------------------------------------------------