github.com/koko1123/flow-go-1@v0.29.6/utils/test_matrix/test_matrix.go (about)

     1  package main
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"os"
     7  	"strings"
     8  
     9  	"golang.org/x/tools/go/packages"
    10  )
    11  
    12  const flowPackagePrefix = "github.com/koko1123/flow-go-1/"
    13  const ciMatrixName = "dynamicMatrix"
    14  
    15  // testMatrix represents a single GitHub Actions test matrix combination that consists of a name and a list of flow-go packages associated with that name.
    16  type testMatrix struct {
    17  	Name     string `json:"name"`
    18  	Packages string `json:"packages"`
    19  }
    20  
    21  // Generates a list of packages to test that will be passed to GitHub Actions
    22  func main() {
    23  	if len(os.Args) == 1 {
    24  		fmt.Fprintln(os.Stderr, "must have at least 1 package listed")
    25  		return
    26  	}
    27  
    28  	allFlowPackages := listAllFlowPackages()
    29  
    30  	targetPackages, seenPackages := listTargetPackages(os.Args[1:], allFlowPackages)
    31  
    32  	otherPackages := listOtherPackages(allFlowPackages, seenPackages)
    33  
    34  	testMatrix := generateTestMatrix(targetPackages, otherPackages)
    35  
    36  	// generate JSON output that will be read in by CI matrix
    37  	// can't use json.MarshalIndent because fromJSON() in CI can’t read JSON with any spaces
    38  	testMatrixBytes, err := json.Marshal(testMatrix)
    39  	if err != nil {
    40  		panic(err)
    41  	}
    42  
    43  	// this string will be read by CI to generate groups of tests to run in separate CI jobs
    44  	testMatrixStr := "::set-output name=" + ciMatrixName + "::" + string(testMatrixBytes)
    45  
    46  	// very important to add newline character at the end of the compacted JSON - otherwise fromJSON() in CI will throw unmarshalling error
    47  	fmt.Println(testMatrixStr)
    48  }
    49  
    50  func generateTestMatrix(targetPackages map[string][]string, otherPackages []string) []testMatrix {
    51  
    52  	var testMatrices []testMatrix
    53  
    54  	for names := range targetPackages {
    55  		targetTestMatrix := testMatrix{
    56  			Name:     names,
    57  			Packages: strings.Join(targetPackages[names], " "),
    58  		}
    59  		testMatrices = append(testMatrices, targetTestMatrix)
    60  	}
    61  
    62  	// add the other packages after all target packages added
    63  	otherTestMatrix := testMatrix{
    64  		Name:     "others",
    65  		Packages: strings.Join(otherPackages, " "),
    66  	}
    67  
    68  	testMatrices = append(testMatrices, otherTestMatrix)
    69  
    70  	return testMatrices
    71  }
    72  
    73  // listTargetPackages returns a map-list of target packages to run as separate CI jobs, based on a list of target package prefixes.
    74  // It also returns a list of the "seen" packages that can then be used to extract the remaining packages to run (in a separate CI job).
    75  func listTargetPackages(targetPackagePrefixes []string, allFlowPackages []string) (map[string][]string, map[string]string) {
    76  	targetPackages := make(map[string][]string)
    77  
    78  	// Stores list of packages already seen / allocated to other lists. Needed for the last package which will
    79  	// have all the leftover packages that weren't allocated to a separate list (CI job).
    80  	// It's a map, not a list, to make it easier to check if a package was seen or not.
    81  	seenPackages := make(map[string]string)
    82  
    83  	// iterate over the target packages to run as separate CI jobs
    84  	for _, targetPackagePrefix := range targetPackagePrefixes {
    85  		var targetPackage []string
    86  
    87  		// go through all packages to see which ones to pull out
    88  		for _, allPackage := range allFlowPackages {
    89  			if strings.HasPrefix(allPackage, flowPackagePrefix+targetPackagePrefix) {
    90  				targetPackage = append(targetPackage, allPackage)
    91  				seenPackages[allPackage] = allPackage
    92  			}
    93  		}
    94  		if len(targetPackage) == 0 {
    95  			panic("no packages exist with prefix " + targetPackagePrefix)
    96  		}
    97  		targetPackages[targetPackagePrefix] = targetPackage
    98  	}
    99  	return targetPackages, seenPackages
   100  }
   101  
   102  // listOtherPackages compiles the remaining packages that don't match any of the target packages.
   103  func listOtherPackages(allFlowPackages []string, seenPackages map[string]string) []string {
   104  	var otherPackages []string
   105  
   106  	for _, allFlowPackage := range allFlowPackages {
   107  		_, seen := seenPackages[allFlowPackage]
   108  		if !seen {
   109  			otherPackages = append(otherPackages, allFlowPackage)
   110  		}
   111  	}
   112  
   113  	if len(otherPackages) == 0 {
   114  		panic("other packages list can't be 0")
   115  	}
   116  	return otherPackages
   117  }
   118  
   119  func listAllFlowPackages() []string {
   120  	flowPackages, err := packages.Load(&packages.Config{}, "./...")
   121  
   122  	if err != nil {
   123  		panic(err)
   124  	}
   125  	var flowPackagesStr []string
   126  	for _, p := range flowPackages {
   127  		flowPackagesStr = append(flowPackagesStr, p.PkgPath)
   128  	}
   129  	return flowPackagesStr
   130  }