blob: 576fde4fe8a75e78af50706b7e215c7014677202 [file] [log] [blame]
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"flag"
"fmt"
"os"
"path"
"strings"
"cloud.google.com/go/internal/actions/logg"
"golang.org/x/mod/modfile"
)
var (
dir = flag.String("dir", "", "the root directory to evaluate")
mod = flag.String("mod", "", "path to go.mod file relative to root directory")
// List of allowlist module prefixes.
allowlist = []string{
// First party deps.
"cloud.google.com/go",
"github.com/GoogleCloudPlatform/",
"github.com/google/",
"github.com/googleapis/",
"github.com/golang/",
"google.golang.org/",
"golang.org/",
// Third party deps (allowed).
"go.opencensus.io",
"go.opentelemetry.io/",
"gopkg.in/yaml",
"github.com/go-git/go-git",
"github.com/apache/arrow/go",
"github.com/cloudprober/cloudprober", // https://github.com/googleapis/google-cloud-go/issues/9377
// Third party deps (temporary exception(s)).
"go.einride.tech/aip", // https://github.com/googleapis/google-cloud-go/issues/9338
"rsc.io/binaryregexp", // https://github.com/googleapis/google-cloud-go/issues/9376
}
)
func main() {
flag.BoolVar(&logg.Quiet, "q", false, "quiet mode, minimal logging")
flag.Parse()
if *mod == "" {
logg.Fatalf("missing required flag: -mod")
}
rootDir, err := os.Getwd()
if err != nil {
logg.Fatal(err)
}
if *dir != "" {
rootDir = *dir
}
modPath := path.Join(rootDir, *mod)
findings, err := check(modPath)
if err != nil {
logg.Fatal(err)
}
if len(findings) != 0 {
fmt.Println("found disallowed module(s) in direct dependencies:")
fmt.Println()
fmt.Printf("\t%s\n", strings.Join(findings, "\n\t"))
fmt.Println()
os.Exit(1)
}
}
// check reads & parses the specified go.mod, then validates that
// all direct dependencies are part of the allowlist.
func check(file string) ([]string, error) {
data, err := os.ReadFile(file)
if err != nil {
return nil, err
}
m, err := modfile.ParseLax(*mod, data, nil)
if err != nil {
return nil, err
}
var disallowed []string
for _, r := range m.Require {
if r.Indirect {
continue
}
var allowed bool
for _, a := range allowlist {
if strings.HasPrefix(r.Mod.Path, a) {
allowed = true
break
}
}
if !allowed {
disallowed = append(disallowed, r.Mod.Path)
}
}
return disallowed, nil
}