blob: 587d204d21c62d71c4ad8d864ee6e211235bc17a [file] [log] [blame]
#!/usr/bin/env python3
# Copyright 2023 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Generate a remote_services.bazelrc file from a template and RBE config files.
"""
import argparse
import filecmp
import sys
from pathlib import Path
from typing import Dict, Iterable, Sequence
def read_rbe_config_file_lines(lines: Iterable[str]) -> Dict[str, str]:
"""Parser for reading RBE config files.
RBE config files are text files with lines of "VAR=VALUE"
(ignoring whole-line #-comments and blank lines).
Spec details can be found at:
https://github.com/bazelbuild/reclient/blob/main/internal/pkg/rbeflag/rbeflag.go
Args:
lines: lines of config from a file
Returns:
dictionary of key-value pairs read from the config file.
"""
result = {}
for line in lines:
stripped = line.strip()
if stripped and not stripped.startswith("#"):
key, sep, value = stripped.partition("=")
if sep == "=":
result[key] = value
return result
def read_rbe_config_file(cfg: Path) -> Dict[str, str]:
return read_rbe_config_file_lines(cfg.read_text().splitlines())
def _rbe_config_to_template_substitutions(
cfg: Dict[str, str]
) -> Dict[str, str]:
instance = cfg["instance"]
project = instance.split("/")[1]
platform_values = cfg["platform"].split(",")
platform_vars = {}
for pv in platform_values:
k, _, v = pv.partition("=")
platform_vars[k] = v
return {
"remote_instance_name": instance,
"rbe_project": project,
"container_image": platform_vars.get("container-image", ""),
}
def _main_arg_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
description=__doc__, formatter_class=argparse.RawTextHelpFormatter
)
parser.add_argument(
"--output",
help="Output file to write expanded template",
type=Path,
)
parser.add_argument(
"--template",
help="Text file containing Python template string",
type=Path,
)
parser.add_argument(
"--cfgs",
help="Input configuration files to read",
type=Path,
nargs="*",
)
return parser
_MAIN_ARG_PARSER = _main_arg_parser()
def _write_file_if_new_or_changed(path: Path, data: str):
tmpf = Path(str(path) + ".tmp") # assume this is writeable location
tmpf.write_text(data)
if not path.exists() or not filecmp.cmp(path, tmpf):
tmpf.rename(path)
else: # contents are unchanged, leave old timestamp
tmpf.unlink()
def main(argv: Sequence[str]) -> int:
args = _MAIN_ARG_PARSER.parse_args(argv)
config = {}
for f in args.cfgs:
config.update(read_rbe_config_file(f))
format_substitutions = _rbe_config_to_template_substitutions(config)
template_text = args.template.read_text()
formatted_text = template_text.format(**format_substitutions)
args.output.parent.mkdir(parents=True, exist_ok=True)
_write_file_if_new_or_changed(args.output, formatted_text)
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))