...

Source file src/edge-infra.dev/cmd/tools/bzl-cache-rc-gen/main.go

Documentation: edge-infra.dev/cmd/tools/bzl-cache-rc-gen

     1  package main
     2  
     3  import (
     4  	"crypto/md5" //nolint:gosec // not used for security
     5  	"encoding/base64"
     6  	"flag"
     7  	"fmt"
     8  	"log"
     9  	"os"
    10  	"runtime"
    11  	"strings"
    12  
    13  	"github.com/peterbourgon/ff/v3"
    14  
    15  	"edge-infra.dev/pkg/lib/cli/sh"
    16  )
    17  
    18  var (
    19  	storageURL    = "https://storage.googleapis.com"
    20  	ciBucket      = "edge-bzl-cache"
    21  	credsPath     string
    22  	enableCache   bool
    23  	uploadResults bool
    24  	bucket        string
    25  	shell         *sh.Shell
    26  	flags         *flag.FlagSet
    27  )
    28  
    29  func init() {
    30  	shell = sh.New()
    31  	flags = flag.NewFlagSet("bzl-cache-rc-gen", flag.ExitOnError)
    32  
    33  	flags.StringVar(
    34  		&bucket,
    35  		"bucket",
    36  		ciBucket,
    37  		"bucket name to use for cache",
    38  	)
    39  
    40  	flags.StringVar(
    41  		&credsPath,
    42  		"google-creds-path",
    43  		"",
    44  		"path to google creds file, if not using google default credentials",
    45  	)
    46  
    47  	flags.BoolVar(
    48  		&enableCache,
    49  		"enable-cache",
    50  		false,
    51  		"whether or not cache usage should be applied to every Bazel command by default",
    52  	)
    53  
    54  	flags.BoolVar(
    55  		&uploadResults,
    56  		"upload-results",
    57  		false,
    58  		"whether or not results should be uploaded to the cache (R/W) or not (RO)",
    59  	)
    60  
    61  	if err := ff.Parse(flags, os.Args[1:], ff.WithEnvVarNoPrefix()); err != nil {
    62  		log.Fatal("failed to parse config")
    63  	}
    64  }
    65  
    66  func main() {
    67  	bazelrc := ""
    68  
    69  	// if no creds path is provided, use default credentials
    70  	if credsPath == "" {
    71  		bazelrc += "build:cache --google_default_credentials\n"
    72  	} else {
    73  		bazelrc += fmt.Sprintf("build:cache --google_credentials=%s\n", credsPath)
    74  	}
    75  
    76  	if uploadResults {
    77  		bazelrc += "build:cache --remote_upload_local_results=true\n"
    78  	}
    79  
    80  	// determine full remote cache URL by hasing the environment we are
    81  	// creating the bazelrc file for
    82  	host := fmt.Sprintf("%s/%s", storageURL, bucket)
    83  	// add unique workstation hash to end
    84  	host = fmt.Sprintf("%s/%s", host, hashEnvironment())
    85  	bazelrc += fmt.Sprintf("build:cache --remote_cache=%s\n", host)
    86  
    87  	if enableCache {
    88  		bazelrc += "build --config=cache\n"
    89  	}
    90  
    91  	fmt.Println(bazelrc)
    92  }
    93  
    94  func hashEnvironment() string {
    95  	// $CC and Bazel interactions
    96  	// https://github.com/kubernetes/test-infra/blob/master/images/bootstrap/create_bazel_cache_rcs.sh#L46
    97  	// > if $CC is set bazel will use this to detect c/c++ toolchains, otherwise gcc
    98  	// > https://blog.bazel.build/2016/03/31/autoconfiguration.html
    99  	cc := os.Getenv("CC")
   100  	if cc == "" {
   101  		out, err := shell.Run("gcc -dumpversion")
   102  		if err != nil {
   103  			log.Fatal("failed to determine gcc version", out, err)
   104  		}
   105  		cc = strings.TrimSpace(out)
   106  	}
   107  
   108  	// some bazel rules still indirectly depend on host python
   109  	// dont bother checking for python like we do for gcc because
   110  	// it should be installed every
   111  	python, err := shell.Run("python3 --version")
   112  	if err == nil {
   113  		python = strings.TrimSpace(python)
   114  	} else {
   115  		python = "none"
   116  	}
   117  
   118  	// add operationg system to hash to split osx + linux
   119  	//nolint:gosec // not used for security
   120  	md5Hash := md5.Sum([]byte(fmt.Sprintf("%s,%s,%s", cc, python, runtime.GOOS)))
   121  	return base64.StdEncoding.EncodeToString(md5Hash[:])
   122  }
   123  

View as plain text