github.com/google/capslock@v0.2.3-0.20240517042941-dac19fc347c0/analyzer/compare.go (about)

     1  // Copyright 2023 Google LLC
     2  //
     3  // Use of this source code is governed by a BSD-style
     4  // license that can be found in the LICENSE file or at
     5  // https://developers.google.com/open-source/licenses/bsd
     6  
     7  package analyzer
     8  
     9  import (
    10  	"fmt"
    11  	"go/types"
    12  	"os"
    13  	"sort"
    14  
    15  	cpb "github.com/google/capslock/proto"
    16  	"golang.org/x/tools/go/packages"
    17  	"google.golang.org/protobuf/encoding/protojson"
    18  )
    19  
    20  func compare(baselineFilename string, pkgs []*packages.Package, queriedPackages map[*types.Package]struct{}, config *Config) error {
    21  	compareData, err := os.ReadFile(baselineFilename)
    22  	if err != nil {
    23  		return fmt.Errorf("Comparison file should include output from running `%s -output=j`. Error from reading comparison file: %v", programName(), err.Error())
    24  	}
    25  	baseline := new(cpb.CapabilityInfoList)
    26  	err = protojson.Unmarshal(compareData, baseline)
    27  	if err != nil {
    28  		return fmt.Errorf("Comparison file should include output from running `%s -output=j`. Error from parsing comparison file: %v", programName(), err.Error())
    29  	}
    30  	cil := GetCapabilityInfo(pkgs, queriedPackages, config)
    31  	diffCapabilityInfoLists(baseline, cil)
    32  	return nil
    33  }
    34  
    35  type capabilitySet map[cpb.Capability]*cpb.CapabilityInfo
    36  type capabilitiesMap map[string]capabilitySet
    37  
    38  // populateMap takes a CapabilityInfoList and returns a map from package
    39  // directory and capability to a pointer to the corresponding entry in the
    40  // input.
    41  func populateMap(cil *cpb.CapabilityInfoList) capabilitiesMap {
    42  	m := make(capabilitiesMap)
    43  	for _, ci := range cil.GetCapabilityInfo() {
    44  		dir := ci.GetPackageDir()
    45  		capmap := m[dir]
    46  		if capmap == nil {
    47  			capmap = make(capabilitySet)
    48  			m[dir] = capmap
    49  		}
    50  		capmap[ci.GetCapability()] = ci
    51  	}
    52  	return m
    53  }
    54  
    55  func diffCapabilityInfoLists(baseline, current *cpb.CapabilityInfoList) {
    56  	baselineMap := populateMap(baseline)
    57  	currentMap := populateMap(current)
    58  	var packages []string
    59  	for packageName := range baselineMap {
    60  		packages = append(packages, packageName)
    61  	}
    62  	for packageName := range currentMap {
    63  		if _, ok := baselineMap[packageName]; !ok {
    64  			packages = append(packages, packageName)
    65  		}
    66  	}
    67  	sort.Strings(packages)
    68  	var differenceFound bool
    69  	for _, packageName := range packages {
    70  		b := baselineMap[packageName]
    71  		c := currentMap[packageName]
    72  		for capability := range c {
    73  			if _, ok := b[capability]; !ok {
    74  				differenceFound = true
    75  				fmt.Printf("Package %s has new capability %s compared to the baseline.\n",
    76  					packageName, capability)
    77  			}
    78  		}
    79  		for capability := range b {
    80  			if _, ok := c[capability]; !ok {
    81  				differenceFound = true
    82  				fmt.Printf("Package %s no longer has capability %s which was in the baseline.\n",
    83  					packageName, capability)
    84  			}
    85  		}
    86  	}
    87  	if differenceFound {
    88  		os.Exit(1)
    89  	}
    90  	os.Exit(0)
    91  }