get.porter.sh/porter@v1.3.0/magefile.go (about) 1 //go:build mage 2 3 // This is a magefile, and is a "makefile for go". 4 // See https://magefile.org/ 5 package main 6 7 import ( 8 "bytes" 9 "fmt" 10 "go/build" 11 "log" 12 "os" 13 "path/filepath" 14 "runtime" 15 "strconv" 16 "strings" 17 18 "get.porter.sh/magefiles/ci" 19 "get.porter.sh/magefiles/git" 20 "get.porter.sh/magefiles/releases" 21 "get.porter.sh/magefiles/tools" 22 "get.porter.sh/porter/mage/setup" 23 "get.porter.sh/porter/pkg" 24 "get.porter.sh/porter/tests/tester" 25 "github.com/magefile/mage/mg" 26 "github.com/magefile/mage/sh" 27 mageci "github.com/uwu-tools/magex/ci" 28 "github.com/uwu-tools/magex/mgx" 29 magepkg "github.com/uwu-tools/magex/pkg" 30 "github.com/uwu-tools/magex/pkg/archive" 31 "github.com/uwu-tools/magex/pkg/downloads" 32 "github.com/uwu-tools/magex/shx" 33 "github.com/uwu-tools/magex/xplat" 34 "golang.org/x/sync/errgroup" 35 36 // mage:import 37 "get.porter.sh/magefiles/docker" 38 39 // mage:import 40 "get.porter.sh/magefiles/tests" 41 42 // mage:import 43 _ "get.porter.sh/porter/mage/docs" 44 ) 45 46 // Default target to run when none is specified 47 // If not set, running mage will list available targets 48 // var Default = Build 49 50 const ( 51 PKG = "get.porter.sh/porter" 52 GoVersion = ">=1.17" 53 ) 54 55 var must = shx.CommandBuilder{StopOnError: true} 56 57 // Check if we have the right version of Go 58 func CheckGoVersion() { 59 tools.EnforceGoVersion(GoVersion) 60 } 61 62 func GenerateGRPCProtobufs() { 63 mg.Deps(setup.EnsureBufBuild) 64 must.Command("buf", "build").In("proto").RunV() 65 must.Command("buf", "generate").In("proto").RunV() 66 } 67 68 // Builds all code artifacts in the repository 69 func Build() { 70 mg.SerialDeps(InstallBuildTools, BuildPorter, BuildExecMixin, BuildAgent, DocsGen) 71 mg.Deps(GetMixins) 72 } 73 74 func InstallBuildTools() { 75 mg.Deps(setup.EnsureProtobufTools, setup.EnsureGRPCurl, setup.EnsureBufBuild) 76 } 77 78 // Build the porter client and runtime 79 func BuildPorter() { 80 mg.Deps(Tidy, copySchema) 81 82 mgx.Must(releases.BuildAll(PKG, "porter", "bin")) 83 } 84 85 // TODO: add support to decouple dir and command name to magefile repo 86 // TODO: add support for additional ldflags to magefile repo 87 // Build the porter client and runtime with gRPC server enabled 88 func XBuildPorterGRPCServer() { 89 var g errgroup.Group 90 supportedClientGOOS := []string{"linux", "darwin", "windows"} 91 supportedClientGOARCH := []string{"amd64", "arm64"} 92 srcCmd := "porter" 93 srcPath := "./cmd/" + srcCmd 94 outPath := "bin/dev" 95 info := releases.LoadMetadata() 96 ldflags := fmt.Sprintf("-w -X main.includeGRPCServer=true -X %s/pkg.Version=%s -X %s/pkg.Commit=%s", PKG, info.Version, PKG, info.Commit) 97 os.MkdirAll(filepath.Dir(outPath), 0770) 98 for _, goos := range supportedClientGOOS { 99 goos := goos 100 for _, goarch := range supportedClientGOARCH { 101 goarch := goarch 102 g.Go(func() error { 103 cmdName := fmt.Sprintf("%s-api-server-%s-%s", srcCmd, goos, goarch) 104 if goos == "windows" { 105 cmdName = cmdName + ".exe" 106 } 107 out := filepath.Join(outPath, cmdName) 108 return shx.Command("go", "build", "-ldflags", ldflags, "-o", out, srcPath). 109 Env("CGO_ENABLED=0", "GO111MODULE=on", "GOOS="+goos, "GOARCH="+goarch). 110 RunV() 111 }) 112 } 113 } 114 mgx.Must(g.Wait()) 115 } 116 117 func copySchema() { 118 // Copy the porter manifest schema into our templates directory with the other schema 119 // We can't use symbolic links because that doesn't work on windows 120 mgx.Must(shx.Copy("pkg/schema/manifest.schema.json", "pkg/templates/templates/schema.json")) 121 mgx.Must(shx.Copy("pkg/schema/manifest.v1.1.0.schema.json", "pkg/templates/templates/v1.1.0.schema.json")) 122 } 123 124 func Tidy() error { 125 return shx.Run("go", "mod", "tidy") 126 } 127 128 // Build the exec mixin client and runtime 129 func BuildExecMixin() { 130 mgx.Must(releases.BuildAll(PKG, "exec", "bin/mixins/exec")) 131 } 132 133 // Build the porter agent 134 func BuildAgent() { 135 // the agent is only used embedded in a docker container, so we only build for linux 136 releases.XBuild(PKG, "agent", "bin", "linux", "amd64") 137 } 138 139 // Cross compile agent for multiple archs in linux 140 func XBuildAgent() { 141 mg.Deps(BuildAgent) 142 releases.XBuild(PKG, "agent", "bin", "linux", "arm64") 143 } 144 145 // Cross-compile porter and the exec mixin 146 func XBuildAll() { 147 mg.Deps(XBuildPorter, XBuildMixins, XBuildAgent) 148 mg.SerialDeps(XBuildPorterGRPCServer) 149 } 150 151 // Cross-compile porter 152 func XBuildPorter() { 153 mg.Deps(copySchema) 154 releases.XBuildAll(PKG, "porter", "bin") 155 releases.PrepareMixinForPublish("porter") 156 } 157 158 // Cross-compile the exec mixin 159 func XBuildMixins() { 160 releases.XBuildAll(PKG, "exec", "bin/mixins/exec") 161 releases.PrepareMixinForPublish("exec") 162 } 163 164 // Generate cli documentation for the website 165 func DocsGen() { 166 // Remove the generated cli directory so that it can detect deleted files 167 os.RemoveAll("docs/content/docs/references/cli") 168 os.Mkdir("docs/content/docs/references/cli", pkg.FileModeDirectory) 169 170 must.RunV("go", "run", "--tags=docs", "./cmd/porter", "docs") 171 } 172 173 // Cleanup workspace after building or running tests. 174 func Clean() { 175 mg.Deps(tests.DeleteTestCluster) 176 mgx.Must(os.RemoveAll("bin")) 177 } 178 179 // Ensure EnsureMage is installed and on the PATH. 180 func EnsureMage() error { 181 return tools.EnsureMage() 182 } 183 184 func Debug() { 185 releases.LoadMetadata() 186 } 187 188 // ConfigureAgent sets up an Azure DevOps agent with EnsureMage and ensures 189 // that GOPATH/bin is in PATH. 190 func ConfigureAgent() error { 191 return ci.ConfigureAgent() 192 } 193 194 // Install mixins used by tests and example bundles, if not already installed 195 func GetMixins() error { 196 defaultMixinVersion := os.Getenv("MIXIN_TAG") 197 if defaultMixinVersion == "" { 198 defaultMixinVersion = "canary" 199 } 200 201 mixins := []struct { 202 name string 203 url string 204 feed string 205 version string 206 }{ 207 {name: "docker"}, 208 {name: "docker-compose"}, 209 {name: "arm"}, 210 {name: "terraform"}, 211 {name: "kubernetes"}, 212 {name: "helm3", feed: "https://mchorfa.github.io/porter-helm3/atom.xml", version: "v0.1.16"}, 213 } 214 var errG errgroup.Group 215 for _, mixin := range mixins { 216 mixin := mixin 217 mixinDir := filepath.Join("bin/mixins/", mixin.name) 218 if _, err := os.Stat(mixinDir); err == nil { 219 log.Println("Mixin already installed into bin:", mixin.name) 220 continue 221 } 222 223 errG.Go(func() error { 224 log.Println("Installing mixin:", mixin.name) 225 if mixin.version == "" { 226 mixin.version = defaultMixinVersion 227 } 228 var source string 229 if mixin.feed != "" { 230 source = "--feed-url=" + mixin.feed 231 } else { 232 source = "--url=" + mixin.url 233 } 234 return porter("mixin", "install", mixin.name, "--version", mixin.version, source).Run() 235 }) 236 } 237 238 return errG.Wait() 239 } 240 241 // Run a porter command from the bin 242 func porter(args ...string) shx.PreparedCommand { 243 porterPath := filepath.Join("bin", "porter") 244 p := shx.Command(porterPath, args...) 245 246 porterHome, _ := filepath.Abs("bin") 247 p.Cmd.Env = []string{"PORTER_HOME=" + porterHome} 248 249 return p 250 } 251 252 // Update golden test files (unit tests only) to match the new test outputs and re-run the unit tests 253 func UpdateTestfiles() { 254 os.Setenv("PORTER_UPDATE_TEST_FILES", "true") 255 defer os.Unsetenv("PORTER_UPDATE_TEST_FILES") 256 257 // Run tests and update any golden files 258 TestUnit() 259 260 // Re-run the tests with the golden files locked in to make sure everything passes now 261 os.Unsetenv("PORTER_UPDATE_TEST_FILES") 262 TestUnit() 263 } 264 265 // Run all tests known to human-kind 266 func Test() { 267 mg.Deps(TestUnit, TestSmoke, TestIntegration) 268 } 269 270 // Run unit tests and verify integration tests compile 271 func TestUnit() { 272 mg.Deps(copySchema) 273 274 // Only do verbose output of tests when called with `mage -v TestSmoke` 275 v := "" 276 if mg.Verbose() { 277 v = "-v" 278 } 279 280 must.Command("go", "test", v, "./...", "-coverprofile", "coverage-unit.out").CollapseArgs().RunV() 281 282 // Verify integration tests compile since we don't run them automatically on pull requests 283 must.Run("go", "test", "-run=non", "-tags=integration", "./...") 284 285 TestInitWarnings() 286 } 287 288 // Run smoke tests to quickly check if Porter is broken 289 func TestSmoke() error { 290 mg.Deps(copySchema, TryRegisterLocalHostAlias, docker.RestartDockerRegistry, BuildTestMixin) 291 292 // Only do verbose output of tests when called with `mage -v TestSmoke` 293 v := "" 294 if mg.Verbose() { 295 v = "-v" 296 } 297 298 // Adding -count to prevent go from caching the test results. 299 return shx.Command("go", "test", "-count=1", "-timeout=20m", "-tags", "smoke", v, "./tests/smoke/...").CollapseArgs().RunV() 300 } 301 302 // Run grpc service tests 303 func TestGRPCService() { 304 var run string 305 runTest := os.Getenv("PORTER_RUN_TEST") 306 if runTest != "" { 307 run = "-run=" + runTest 308 } 309 310 verbose := "" 311 if mg.Verbose() { 312 verbose = "-v" 313 } 314 must.Command("go", "test", verbose, "-timeout=5m", run, "./tests/grpc/...").CollapseArgs().RunV() 315 } 316 317 func getRegistry() string { 318 registry := os.Getenv("PORTER_REGISTRY") 319 if registry == "" { 320 registry = "localhost:5000" 321 } 322 return registry 323 } 324 325 func BuildImages() { 326 info := releases.LoadMetadata() 327 registry := getRegistry() 328 329 buildImages(registry, info) 330 } 331 332 func buildGRPCProtocImage() { 333 var g errgroup.Group 334 335 enableBuildKit := "DOCKER_BUILDKIT=1" 336 g.Go(func() error { 337 img := "protoc:local" 338 err := shx.Command("docker", "build", "-t", img, "-f", "build/protoc.Dockerfile", "."). 339 Env(enableBuildKit).RunV() 340 if err != nil { 341 return err 342 } 343 return nil 344 }) 345 mgx.Must(g.Wait()) 346 } 347 348 func buildImages(registry string, info releases.GitMetadata) { 349 var g errgroup.Group 350 351 enableBuildKit := "DOCKER_BUILDKIT=1" 352 g.Go(func() error { 353 img := fmt.Sprintf("%s/porter:%s", registry, info.Version) 354 err := shx.Command("docker", "build", "-t", img, "-f", "build/images/client/Dockerfile", "."). 355 Env(enableBuildKit).RunV() 356 if err != nil { 357 return err 358 } 359 360 err = shx.Run("docker", "tag", img, fmt.Sprintf("%s/porter:%s", registry, info.Permalink)) 361 if err != nil { 362 return err 363 } 364 365 // porter-agent does a FROM porter so they can't go in parallel 366 img = fmt.Sprintf("%s/porter-agent:%s", registry, info.Version) 367 err = shx.Command("docker", "build", "-t", img, "--build-arg", "PORTER_VERSION="+info.Version, "--build-arg", "REGISTRY="+registry, "-f", "build/images/agent/Dockerfile", "."). 368 Env(enableBuildKit).RunV() 369 if err != nil { 370 return err 371 } 372 373 return shx.Run("docker", "tag", img, fmt.Sprintf("%s/porter-agent:%s", registry, info.Permalink)) 374 }) 375 376 mgx.Must(g.Wait()) 377 } 378 379 func PublishImages() { 380 mg.Deps(BuildImages) 381 382 info := releases.LoadMetadata() 383 384 pushImagesTo(getRegistry(), info) 385 } 386 387 // Builds the porter-agent image and publishes it to a local test cluster with the Porter Operator. 388 func LocalPorterAgentBuild() { 389 // Publish to the local registry/cluster setup by the Porter Operator. 390 os.Setenv("REGISTRY", "localhost:5000") 391 // Force the image to be pushed to the registry even though it's a local dev build. 392 os.Setenv("PORTER_FORCE_PUBLISH", "true") 393 394 mg.SerialDeps(XBuildPorter, BuildAgent, PublishImages) 395 } 396 397 // Only push tagged versions, canary and latest 398 func pushImagesTo(registry string, info releases.GitMetadata) { 399 if info.IsTaggedRelease { 400 pushImages(registry, info.Version) 401 } 402 403 force, _ := strconv.ParseBool(os.Getenv("PORTER_FORCE_PUBLISH")) 404 if info.ShouldPublishPermalink() || force { 405 pushImages(registry, info.Permalink) 406 } else { 407 fmt.Println("Skipping image publish for permalink", info.Permalink) 408 } 409 } 410 411 func PublishServerMultiArchImages() { 412 registry := getRegistry() 413 info := releases.LoadMetadata() 414 415 if info.IsTaggedRelease { 416 buildAndPushServerMultiArch(registry, info.Version) 417 } else { 418 fmt.Println("Skipping server image publish for not tagged release", info.Version) 419 } 420 421 if info.ShouldPublishPermalink() { 422 buildAndPushServerMultiArch(registry, info.Permalink) 423 } else { 424 fmt.Println("Skipping server image publish for permalink", info.Permalink) 425 } 426 } 427 428 func buildAndPushServerMultiArch(registry string, tag string) { 429 img := fmt.Sprintf("%s/server:%s", registry, tag) 430 must.RunV("docker", "buildx", "create", "--use") 431 must.RunV("docker", "buildx", "bake", "-f", "docker-bake.json", "--push", "--set", "server.tags="+img, "server") 432 } 433 434 // Build a local image for the server based off of local architecture 435 func BuildLocalServerImage() { 436 registry := getRegistry() 437 info := releases.LoadMetadata() 438 goarch := runtime.GOARCH 439 buildServerImage(registry, info, goarch) 440 } 441 442 // Builds an image for the server based off of the goarch 443 func buildServerImage(registry string, info releases.GitMetadata, goarch string) { 444 var platform string 445 switch goarch { 446 case "arm64": 447 platform = "linux/arm64" 448 case "amd64": 449 platform = "linux/amd64" 450 default: 451 platform = "linux/amd64" 452 } 453 img := fmt.Sprintf("%s/server:%s", registry, info.Version) 454 must.RunV("docker", "build", "-f", "build/images/server/Dockerfile", "-t", img, "--platform="+platform, ".") 455 } 456 457 func pushImages(registry string, tag string) { 458 pushImage(fmt.Sprintf("%s/porter:%s", registry, tag)) 459 pushImage(fmt.Sprintf("%s/porter-agent:%s", registry, tag)) 460 } 461 462 func pushImage(img string) { 463 must.RunV("docker", "push", img) 464 } 465 466 // Publish the porter binaries and install scripts. 467 func PublishPorter() { 468 mg.Deps(tools.EnsureGitHubClient, releases.ConfigureGitBot) 469 470 info := releases.LoadMetadata() 471 472 // Copy install scripts into version directory 473 // Rewrites the version number in the script uploaded to the github release 474 // If it's a tagged version, we reference that in the script 475 // Otherwise reference the name of the build, e.g. "canary" 476 scriptVersion := info.Version 477 if !info.IsTaggedRelease { 478 scriptVersion = info.Permalink 479 } 480 must.Command("./scripts/prep-install-scripts.sh").Env("VERSION=" + scriptVersion).RunV() 481 482 porterVersionDir := filepath.Join("bin", info.Version) 483 execVersionDir := filepath.Join("bin/mixins/exec", info.Version) 484 var repo = os.Getenv("PORTER_RELEASE_REPOSITORY") 485 if repo == "" { 486 repo = "github.com/getporter/porter" 487 } 488 remote := fmt.Sprintf("https://%s.git", repo) 489 490 // Create or update GitHub release for the permalink (canary/latest) with the version's assets (porter binaries, exec binaries and install scripts) 491 if info.ShouldPublishPermalink() { 492 // Move the permalink tag. The existing release automatically points to the tag. 493 must.RunV("git", "tag", "-f", info.Permalink, info.Version) 494 must.RunV("git", "push", "-f", remote, info.Permalink) 495 496 releases.AddFilesToRelease(repo, info.Permalink, porterVersionDir) 497 releases.AddFilesToRelease(repo, info.Permalink, execVersionDir) 498 } else { 499 fmt.Println("Skipping publish binaries for permalink", info.Permalink) 500 } 501 502 if info.IsTaggedRelease { 503 // Create GitHub release for the exact version (v1.2.3) and attach assets 504 releases.AddFilesToRelease(repo, info.Version, porterVersionDir) 505 releases.AddFilesToRelease(repo, info.Version, execVersionDir) 506 } else { 507 fmt.Println("Skipping publish binaries for not tagged release", info.Version) 508 } 509 } 510 511 // Publish internal porter mixins, like exec. 512 func PublishMixins() { 513 releases.PublishMixinFeed("exec") 514 } 515 516 // Copy the cross-compiled binaries from xbuild into bin. 517 func UseXBuildBinaries() error { 518 pwd, _ := os.Getwd() 519 goos := build.Default.GOOS 520 ext := "" 521 if runtime.GOOS == "windows" { 522 ext = ".exe" 523 } 524 525 copies := map[string]string{ 526 "bin/dev/porter-$GOOS-amd64$EXT": "bin/porter$EXT", 527 "bin/dev/porter-linux-amd64": "bin/runtimes/porter-runtime", 528 "bin/mixins/exec/dev/exec-$GOOS-amd64$EXT": "bin/mixins/exec/exec$EXT", 529 "bin/mixins/exec/dev/exec-linux-amd64": "bin/mixins/exec/runtimes/exec-runtime", 530 } 531 532 r := strings.NewReplacer("$GOOS", goos, "$EXT", ext, "$PWD", pwd) 533 for src, dest := range copies { 534 src = r.Replace(src) 535 dest = r.Replace(dest) 536 log.Printf("Copying %s to %s", src, dest) 537 538 destDir := filepath.Dir(dest) 539 os.MkdirAll(destDir, pkg.FileModeDirectory) 540 541 err := sh.Copy(dest, src) 542 if err != nil { 543 return err 544 } 545 } 546 547 return SetBinExecutable() 548 } 549 550 // Run `chmod +x -R bin`. 551 func SetBinExecutable() error { 552 err := chmodRecursive("bin", pkg.FileModeExecutable) 553 if err != nil { 554 return fmt.Errorf("could not set +x on the test bin: %w", err) 555 } 556 557 return nil 558 } 559 560 func chmodRecursive(name string, mode os.FileMode) error { 561 return filepath.Walk(name, func(path string, info os.FileInfo, err error) error { 562 if err != nil { 563 return err 564 } 565 566 log.Println("chmod +x ", path) 567 return os.Chmod(path, mode) 568 }) 569 } 570 571 // Run integration tests (slow). 572 func TestIntegration() { 573 mg.Deps(tests.EnsureTestCluster, copySchema, TryRegisterLocalHostAlias, BuildTestMixin, BuildTestPlugin, EnsureCosign, EnsureNotation) 574 575 var run string 576 runTest := os.Getenv("PORTER_RUN_TEST") 577 if runTest != "" { 578 run = "-run=" + runTest 579 } 580 581 verbose := "" 582 if mg.Verbose() { 583 verbose = "-v" 584 } 585 586 var path string 587 filename := os.Getenv("PORTER_INTEG_FILE") 588 if filename == "" { 589 path = "./..." 590 } else { 591 path = "./tests/integration/" + filename 592 } 593 594 must.Command("go", "test", verbose, "-timeout=30m", run, "-tags=integration", path, "-coverprofile", "coverage-integration.out").CollapseArgs().RunV() 595 } 596 597 func TestInitWarnings() { 598 // This is hard to test in a normal unit test because we need to build porter with custom build tags, 599 // so I'm testing it in the magefile directly in a way that doesn't leave around an unsafe Porter binary. 600 601 // Verify that running Porter with traceSensitiveAttributes set that a warning is printed 602 fmt.Println("Validating traceSensitiveAttributes warning") 603 output := &bytes.Buffer{} 604 must.Command("go", "run", "-tags=traceSensitiveAttributes", "./cmd/porter", "schema"). 605 Stderr(output).Stdout(output).Exec() 606 if !strings.Contains(output.String(), "WARNING! This is a custom developer build of Porter with the traceSensitiveAttributes build flag set") { 607 fmt.Printf("Got output: %s\n", output.String()) 608 panic("Expected a build of Porter with traceSensitiveAttributes build tag set to warn at startup but it didn't") 609 } 610 } 611 612 // TryRegisterLocalHostAlias edits /etc/hosts to use porter-test-registry hostname alias 613 // This is not safe to call more than once and is intended for use on the CI server only 614 func TryRegisterLocalHostAlias() { 615 if _, isCI := mageci.DetectBuildProvider(); !isCI { 616 return 617 } 618 619 err := shx.RunV("sudo", "bash", "-c", "echo 127.0.0.1 porter-test-registry >> /etc/hosts") 620 if err != nil { 621 fmt.Println("skipping registering the porter-test-registry hostname alias: could not write to /etc/hosts") 622 return 623 } 624 625 fmt.Println("Added host alias porter-test-registry to /etc/hosts") 626 os.Setenv(tester.TestRegistryAlias, "porter-test-registry") 627 } 628 629 func BuildTestPlugin() { 630 must.RunV("go", "build", "-o", "bin/testplugin", "./cmd/testplugin") 631 } 632 633 func BuildTestMixin() { 634 os.MkdirAll("bin/mixins/testmixin", 0770) 635 must.RunV("go", "build", "-o", "bin/mixins/testmixin/testmixin"+xplat.FileExt(), "./cmd/testmixin") 636 } 637 638 // Copy the locally built porter and exec binaries to PORTER_HOME 639 func Install() { 640 porterHome := getPorterHome() 641 fmt.Println("installing Porter from bin to", porterHome) 642 643 // Copy porter binaries 644 mgx.Must(os.MkdirAll(porterHome, pkg.FileModeDirectory)) 645 646 // HACK: Works around a codesigning problem on Apple Silicon where overwriting a binary that has already been executed doesn't cause the corresponding codesign entry in the OS cache to update 647 // Mac then prevents the updated binary from running because the signature doesn't match 648 // Removing the file first clears the cache so that we don't run into "zsh: killed porter..." 649 // See https://stackoverflow.com/questions/67378106/mac-m1-cping-binary-over-another-results-in-crash 650 // See https://openradar.appspot.com/FB8914231 651 removeError := os.Remove(filepath.Join(porterHome, "porter"+xplat.FileExt())) 652 if !os.IsNotExist(removeError) { 653 mgx.Must(removeError) 654 } 655 removeError = os.RemoveAll(filepath.Join(porterHome, "runtimes")) 656 if !os.IsNotExist(removeError) { 657 mgx.Must(removeError) 658 } 659 660 // Okay now it's safe to copy these files over 661 mgx.Must(shx.Copy(filepath.Join("bin", "porter"+xplat.FileExt()), porterHome)) 662 mgx.Must(shx.Copy(filepath.Join("bin", "runtimes"), porterHome, shx.CopyRecursive)) 663 664 // Copy mixin binaries 665 mixinsDir := filepath.Join("bin", "mixins") 666 mixinsDirItems, err := os.ReadDir(mixinsDir) 667 if err != nil { 668 mgx.Must(fmt.Errorf("could not list mixins in bin: %w", err)) 669 } 670 671 for _, fi := range mixinsDirItems { 672 // do not install the test mixins 673 if fi.Name() == "testmixin" { 674 continue 675 } 676 677 if !fi.IsDir() { 678 continue 679 } 680 681 mixin := fi.Name() 682 srcDir := filepath.Join(mixinsDir, mixin) 683 destDir := filepath.Join(porterHome, "mixins", mixin) 684 mgx.Must(os.MkdirAll(destDir, pkg.FileModeDirectory)) 685 686 // HACK: Works around a codesigning problem on Apple Silicon where overwriting a binary that has already been executed doesn't cause the corresponding codesign entry in the OS cache to update 687 // Mac then prevents the updated binary from running because the signature doesn't match 688 // Removing the file first clears the cache so that we don't run into "zsh: killed MIXIN..." 689 // See https://stackoverflow.com/questions/67378106/mac-m1-cping-binary-over-another-results-in-crash 690 // See https://openradar.appspot.com/FB8914231 691 692 // Copy the mixin client binary 693 mgx.Must(shx.Copy(filepath.Join(srcDir, mixin+xplat.FileExt()), destDir)) 694 695 // Copy the mixin runtimes 696 mgx.Must(shx.Copy(filepath.Join(srcDir, "runtimes"), destDir, shx.CopyRecursive)) 697 } 698 } 699 700 // Run Go Vet on the project 701 func Vet() { 702 must.RunV("go", "vet", "./...") 703 } 704 705 // Run golangci-lint on the project 706 func Lint() { 707 mg.Deps(tools.EnsureGolangCILint) 708 must.RunV("golangci-lint", "run", "--max-issues-per-linter", "0", "--max-same-issues", "0", "./...") 709 } 710 711 func getPorterHome() string { 712 porterHome := os.Getenv("PORTER_HOME") 713 if porterHome == "" { 714 home, err := os.UserHomeDir() 715 if err != nil { 716 mgx.Must(fmt.Errorf("could not determine home directory: %w", err)) 717 } 718 719 porterHome = filepath.Join(home, ".porter") 720 } 721 return porterHome 722 } 723 724 // SetupDCO configures your git repository to automatically sign your commits 725 // to comply with our DCO 726 func SetupDCO() error { 727 return git.SetupDCO() 728 } 729 730 func EnsureCosign() { 731 if ok, _ := magepkg.IsCommandAvailable("cosign", "version", "v2.2.2"); ok { 732 return 733 } 734 735 opts := downloads.DownloadOptions{ 736 UrlTemplate: "https://github.com/sigstore/cosign/releases/download/v{{.VERSION}}/cosign-{{.GOOS}}-{{.GOARCH}}{{.EXT}}", 737 Name: "cosign", 738 Version: "2.2.2", 739 } 740 741 if runtime.GOOS == "windows" { 742 opts.Ext = ".exe" 743 } 744 745 err := downloads.DownloadToGopathBin(opts) 746 mgx.Must(err) 747 } 748 749 func EnsureNotation() { 750 if ok, _ := magepkg.IsCommandAvailable("notation", "version", "1.1.0"); ok { 751 return 752 } 753 754 target := "notation{{.EXT}}" 755 if runtime.GOOS == "windows" { 756 target = "notation.exe" 757 } 758 759 opts := archive.DownloadArchiveOptions{ 760 DownloadOptions: downloads.DownloadOptions{ 761 UrlTemplate: "https://github.com/notaryproject/notation/releases/download/v{{.VERSION}}/notation_{{.VERSION}}_{{.GOOS}}_{{.GOARCH}}{{.EXT}}", 762 Name: "notation", 763 Version: "1.1.0", 764 }, 765 ArchiveExtensions: map[string]string{ 766 "linux": ".tar.gz", 767 "darwin": ".tar.gz", 768 "windows": ".zip", 769 }, 770 TargetFileTemplate: target, 771 } 772 err := archive.DownloadToGopathBin(opts) 773 mgx.Must(err) 774 }