Files
lotus/cmd/ci/main.go
Piotr Galar ea29eed4d1 feat(ci): run unit test jobs on arm64 machines additionally (#12864)
* chore: move test metadata retrieval to a new Go CLI tool

* ci: install go before retrieving test group metadata

* ci: run unit tests on arm64 machines

* ci: fix the packages metadata

* ci: cache deps per runner os and arch

* chore: rename name to testGroupName where applicable

* chore: add createPackagePath helper

* chore: turn runners into an enumerated type

* chore: sort testGroupNamesToRunners

* chore: rename find/getIntegrationTestGroups

* chore: use for-loop to get unit and other test groups

* chore: reorganize the ci cli

* chore: rename list test groups

* chore: restore the runner selection

* chore: resolve pull request review comments
2025-02-23 18:34:47 +01:00

351 lines
10 KiB
Go

package main
import (
"encoding/json"
"os"
"path/filepath"
"strings"
log "github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
)
type TestGroupExecutionContext struct {
Name string `json:"name"`
Runner Runner `json:"runner"`
}
// Runner is a list because it is a list of labels associated with a single runner.
type Runner []string
var (
linux_x64_4xlarge = []string{"self-hosted", "linux", "x64", "4xlarge"}
linux_x64_2xlarge = []string{"self-hosted", "linux", "x64", "2xlarge"}
linux_x64_xlarge = []string{"self-hosted", "linux", "x64", "xlarge"}
linux_arm64_2xlarge = []string{"self-hosted", "linux", "arm64", "2xlarge"}
linux_arm64_xlarge = []string{"self-hosted", "linux", "arm64", "xlarge"}
linux_x64 = []string{"self-hosted", "linux", "x64"}
linux_arm64 = []string{"self-hosted", "linux", "arm64"}
)
type TestGroupMetadata struct {
Packages []string `json:"packages"`
NeedsParameters bool `json:"needs_parameters"`
SkipConformance bool `json:"skip_conformance"`
TestRustProofLogs bool `json:"test_rust_proof_logs"`
Format string `json:"format"`
GoTestFlags string `json:"go_test_flags"`
}
func main() {
app := &cli.App{
Name: "ci",
Usage: "Lotus CI tool",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "json",
Usage: "Format output as JSON",
},
},
Before: func(c *cli.Context) error {
if c.Bool("json") {
log.SetFormatter(&log.JSONFormatter{})
} else {
log.SetFormatter(&log.TextFormatter{
TimestampFormat: "2006-01-02 15:04:05",
FullTimestamp: true,
})
}
log.SetOutput(os.Stdout)
return nil
},
Commands: []*cli.Command{
{
Name: "list-test-group-execution-contexts",
Usage: "List all test group execution contexts",
Action: func(c *cli.Context) error {
integrationTestGroups, err := getIntegrationTestGroups()
if err != nil {
return err
}
unitTestGroups := getUnitTestGroups()
otherTestGroups := getOtherTestGroups()
groups := append(append(integrationTestGroups, unitTestGroups...), otherTestGroups...)
b, err := json.MarshalIndent(groups, "", " ")
if err != nil {
log.Fatal(err)
}
log.Info(string(b))
return nil
},
},
{
Name: "get-test-group-metadata",
Usage: "Get the metadata for a test group",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "name",
Usage: "Name of the test group",
Required: true,
},
},
Action: func(c *cli.Context) error {
testGroupName := c.String("name")
metadata := getTestGroupMetadata(testGroupName)
b, err := json.MarshalIndent(metadata, "", " ")
if err != nil {
log.Fatal(err)
}
log.Info(string(b))
return nil
},
},
},
}
if err := app.Run(os.Args); err != nil {
log.Fatal(err)
}
}
func getIntegrationTestGroups() ([]TestGroupExecutionContext, error) {
groups := []TestGroupExecutionContext{}
err := filepath.Walk("itests", func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && strings.HasSuffix(info.Name(), "_test.go") {
parts := strings.Split(path, string(os.PathSeparator))
testGroupName := strings.Join([]string{"itest", strings.TrimSuffix(parts[1], "_test.go")}, "-")
groups = append(groups, getTestGroups(testGroupName)...)
}
return nil
})
if err != nil {
return nil, err
}
return groups, nil
}
func getUnitTestGroups() []TestGroupExecutionContext {
groups := []TestGroupExecutionContext{}
for _, value := range []string{"cli", "node", "rest", "storage"} {
groups = append(groups, getTestGroups("unit-"+value)...)
}
return groups
}
func getOtherTestGroups() []TestGroupExecutionContext {
groups := []TestGroupExecutionContext{}
for _, value := range []string{"conformance", "multicore-sdr"} {
groups = append(groups, getTestGroups(value)...)
}
return groups
}
func getTestGroups(testGroupName string) []TestGroupExecutionContext {
runners := getRunners(testGroupName)
groups := []TestGroupExecutionContext{}
for _, runner := range runners {
groups = append(groups, TestGroupExecutionContext{
Name: testGroupName,
Runner: runner,
})
}
return groups
}
func getRunners(testGroupName string) []Runner {
if os.Getenv("GITHUB_REPOSITORY_OWNER") != "filecoin-project" {
return []Runner{linux_x64}
}
testGroupNameToRunners := map[string][]Runner{
"itest-cli": {linux_x64_xlarge},
"itest-deals_invalid_utf8_label": {linux_x64_xlarge},
"itest-decode_params": {linux_x64_xlarge},
"itest-dup_mpool_messages": {linux_x64_xlarge},
"itest-eth_account_abstraction": {linux_x64_xlarge},
"itest-eth_api": {linux_x64_xlarge},
"itest-eth_balance": {linux_x64_xlarge},
"itest-eth_bytecode": {linux_x64_xlarge},
"itest-eth_config": {linux_x64_xlarge},
"itest-eth_conformance": {linux_x64_xlarge},
"itest-eth_deploy": {linux_x64_xlarge},
"itest-eth_fee_history": {linux_x64_xlarge},
"itest-eth_transactions": {linux_x64_xlarge},
"itest-fevm_address": {linux_x64_xlarge},
"itest-fevm_events": {linux_x64_xlarge},
"itest-gas_estimation": {linux_x64_xlarge},
"itest-gateway": {linux_x64_2xlarge},
"itest-get_messages_in_ts": {linux_x64_xlarge},
"itest-lite_migration": {linux_x64_xlarge},
"itest-lookup_robust_address": {linux_x64_xlarge},
"itest-manual_onboarding": {linux_x64_4xlarge},
"itest-mempool": {linux_x64_xlarge},
"itest-mpool_msg_uuid": {linux_x64_xlarge},
"itest-mpool_push_with_uuid": {linux_x64_xlarge},
"itest-msgindex": {linux_x64_xlarge},
"itest-multisig": {linux_x64_xlarge},
"itest-net": {linux_x64_xlarge},
"itest-niporep_manual": {linux_x64_4xlarge},
"itest-nonce": {linux_x64_xlarge},
"itest-path_detach_redeclare": {linux_x64_xlarge},
"itest-pending_deal_allocation": {linux_x64_xlarge},
"itest-remove_verifreg_datacap": {linux_x64_xlarge},
"itest-sector_import_full": {linux_x64_2xlarge},
"itest-sector_import_simple": {linux_x64_2xlarge},
"itest-sector_miner_collateral": {linux_x64_xlarge},
"itest-sector_numassign": {linux_x64_xlarge},
"itest-sector_pledge": {linux_x64_4xlarge},
"itest-self_sent_txn": {linux_x64_xlarge},
"itest-verifreg": {linux_x64_xlarge},
"itest-wdpost": {linux_x64_2xlarge},
"itest-worker": {linux_x64_4xlarge},
"multicore-sdr": {linux_x64_xlarge},
"unit-cli": {linux_x64, linux_arm64},
"unit-node": {linux_x64_xlarge, linux_arm64_xlarge},
"unit-rest": {linux_x64, linux_arm64},
"unit-storage": {linux_x64_2xlarge, linux_arm64_2xlarge},
}
if runners, ok := testGroupNameToRunners[testGroupName]; ok {
return runners
}
return []Runner{linux_x64}
}
func getTestGroupMetadata(testGroupName string) TestGroupMetadata {
packages := getPackages(testGroupName)
needsParameters := getNeedsParameters(testGroupName)
skipConformance := getSkipConformance(testGroupName)
testRustProofLogs := getTestRustProofLogs(testGroupName)
format := getFormat()
goTestFlags := getGoTestFlags(testGroupName)
return TestGroupMetadata{
Packages: packages,
NeedsParameters: needsParameters,
SkipConformance: skipConformance,
TestRustProofLogs: testRustProofLogs,
Format: format,
GoTestFlags: goTestFlags,
}
}
func getPackages(testGroupName string) []string {
if strings.HasPrefix(testGroupName, "itest-") {
return []string{createPackagePath("itests", strings.Join([]string{strings.TrimPrefix(testGroupName, "itest-"), "test.go"}, "_"))}
}
testGroupNameToPackages := map[string][]string{
"multicore-sdr": {createPackagePath("storage", "sealer", "ffiwrapper")},
"conformance": {createPackagePath("conformance")},
"unit-cli": {
createPackagePath("cli", "..."),
createPackagePath("cmd", "..."),
createPackagePath("api", "..."),
},
"unit-storage": {
createPackagePath("storage", "..."),
createPackagePath("extern", "..."),
},
"unit-node": {
createPackagePath("node", "..."),
},
"unit-rest": {
createPackagePath("blockstore", "..."),
createPackagePath("build", "..."),
createPackagePath("chain", "..."),
createPackagePath("conformance", "..."),
createPackagePath("gateway", "..."),
createPackagePath("journal", "..."),
createPackagePath("lib", "..."),
createPackagePath("paychmgr", "..."),
createPackagePath("tools", "..."),
},
}
return testGroupNameToPackages[testGroupName]
}
func getNeedsParameters(testGroupName string) bool {
testGroupNames := []string{
"conformance",
"itest-api",
"itest-direct_data_onboard_verified",
"itest-direct_data_onboard",
"itest-manual_onboarding",
"itest-niporep_manual",
"itest-net",
"itest-path_detach_redeclare",
"itest-sealing_resources",
"itest-sector_import_full",
"itest-sector_import_simple",
"itest-sector_pledge",
"itest-sector_unseal",
"itest-wdpost_no_miner_storage",
"itest-wdpost_worker_config",
"itest-wdpost",
"itest-worker",
"multicore-sdr",
"unit-cli",
"unit-storage",
}
return contains(testGroupNames, testGroupName)
}
func getSkipConformance(testGroupName string) bool {
testGroupNames := []string{
"conformance",
}
return !contains(testGroupNames, testGroupName)
}
func getTestRustProofLogs(testGroupName string) bool {
testGroupNames := []string{
"multicore-sdr",
}
return contains(testGroupNames, testGroupName)
}
func getFormat() string {
return "standard-verbose"
}
func getGoTestFlags(testGroupName string) string {
testGroupNameToFlags := map[string]string{
"multicore-sdr": "-run=TestMulticoreSDR",
"conformance": "-run=TestConformance",
}
if flag, ok := testGroupNameToFlags[testGroupName]; ok {
return flag
}
return ""
}
func createPackagePath(pathParts ...string) string {
return strings.Join(append([]string{"."}, pathParts...), string(os.PathSeparator))
}
func contains(slice []string, item string) bool {
for _, s := range slice {
if s == item {
return true
}
}
return false
}