app_build.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. package v2
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "os"
  7. "strings"
  8. "github.com/fatih/color"
  9. api "github.com/porter-dev/porter/api/client"
  10. "github.com/porter-dev/porter/cli/cmd/config"
  11. v2 "github.com/porter-dev/porter/internal/porter_app/v2"
  12. )
  13. // AppBuildInput is the input to the AppBuild function
  14. type AppBuildInput struct {
  15. // CLIConfig is the CLI configuration
  16. CLIConfig config.CLIConfig
  17. // Client is the Porter API client
  18. Client api.Client
  19. // AppName is the name of the app
  20. AppName string
  21. // DeploymentTargetName is the name of the deployment target, if provided
  22. DeploymentTargetName string
  23. // BuildMethod is the build method for the app on apply, either 'docker' or 'pack'
  24. BuildMethod string
  25. // Dockerfile is the path to the Dockerfile when build method is 'docker'
  26. Dockerfile string
  27. // Builder is the builder to use when build method is 'pack'
  28. Builder string
  29. // Buildpacks is the buildpacks to use when build method is 'pack'
  30. Buildpacks []string
  31. // BuildContext is the build context for the app, e.g. ./app
  32. BuildContext string
  33. // ImageTag is the image tag to use for the app build
  34. ImageTag string
  35. // PatchOperations is the set of patch operations to apply to the app build
  36. PatchOperations []v2.PatchOperation
  37. }
  38. // AppBuild builds an app using a combination of the provided flag values and build settings from the latest app revision
  39. func AppBuild(ctx context.Context, inp AppBuildInput) error {
  40. cliConf := inp.CLIConfig
  41. client := inp.Client
  42. if cliConf.Project == 0 {
  43. return errors.New("project must be set")
  44. }
  45. if cliConf.Cluster == 0 {
  46. return errors.New("cluster must be set")
  47. }
  48. latest, err := client.CurrentAppRevision(ctx, api.CurrentAppRevisionInput{
  49. ProjectID: cliConf.Project,
  50. ClusterID: cliConf.Cluster,
  51. AppName: inp.AppName,
  52. DeploymentTargetName: inp.DeploymentTargetName,
  53. })
  54. if err != nil {
  55. return fmt.Errorf("error getting latest app revision: %s", err)
  56. }
  57. buildSettings, err := client.GetBuildFromRevision(ctx, api.GetBuildFromRevisionInput{
  58. ProjectID: cliConf.Project,
  59. ClusterID: cliConf.Cluster,
  60. AppName: inp.AppName,
  61. AppRevisionID: latest.AppRevision.ID,
  62. PatchOperations: inp.PatchOperations,
  63. })
  64. if err != nil {
  65. return fmt.Errorf("error getting build from revision: %w", err)
  66. }
  67. tagForBuild, err := tagFromCommitSHAOrFlag(inp.ImageTag)
  68. if err != nil {
  69. return fmt.Errorf("error getting tag for build: %w", err)
  70. }
  71. buildEnvVariables := make(map[string]string)
  72. for k, v := range buildSettings.BuildEnvVariables {
  73. buildEnvVariables[k] = v
  74. }
  75. // use all env variables from running container in build
  76. env := os.Environ()
  77. for _, v := range env {
  78. pair := strings.SplitN(v, "=", 2)
  79. if len(pair) == 2 {
  80. if strings.HasPrefix(pair[0], "PORTER_") || strings.HasPrefix(pair[0], "NEXT_PUBLIC_") {
  81. buildEnvVariables[pair[0]] = pair[1]
  82. }
  83. }
  84. }
  85. buildInput, err := buildInputFromBuildSettings(buildInputFromBuildSettingsInput{
  86. projectID: cliConf.Project,
  87. appName: inp.AppName,
  88. commitSHA: tagForBuild,
  89. image: buildSettings.Image,
  90. build: buildSettings.Build,
  91. buildEnv: buildEnvVariables,
  92. })
  93. if err != nil {
  94. return fmt.Errorf("error creating build input from build settings: %w", err)
  95. }
  96. // skip push when only a build is requested
  97. buildInput.SkipPush = true
  98. buildOutput := build(ctx, client, buildInput)
  99. if buildOutput.Error != nil {
  100. return fmt.Errorf("error building app: %w", buildOutput.Error)
  101. }
  102. color.New(color.FgGreen).Printf("Successfully built image (tag: %s)\n", tagForBuild) // nolint:errcheck,gosec
  103. return nil
  104. }
  105. func tagFromCommitSHAOrFlag(providedTag string) (string, error) {
  106. tag := commitSHAFromEnv()
  107. if providedTag != "" {
  108. tag = providedTag
  109. }
  110. if tag == "" {
  111. return tag, errors.New("no tag set and could not determine latest commit SHA")
  112. }
  113. return tag, nil
  114. }