2
0

app_build.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  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. // PullImageBeforeBuild is a flag indicating whether to pull the previous image before building
  38. PullImageBeforeBuild bool
  39. }
  40. // AppBuild builds an app using a combination of the provided flag values and build settings from the latest app revision
  41. func AppBuild(ctx context.Context, inp AppBuildInput) error {
  42. cliConf := inp.CLIConfig
  43. client := inp.Client
  44. if cliConf.Project == 0 {
  45. return errors.New("project must be set")
  46. }
  47. if cliConf.Cluster == 0 {
  48. return errors.New("cluster must be set")
  49. }
  50. latest, err := client.CurrentAppRevision(ctx, api.CurrentAppRevisionInput{
  51. ProjectID: cliConf.Project,
  52. ClusterID: cliConf.Cluster,
  53. AppName: inp.AppName,
  54. DeploymentTargetName: inp.DeploymentTargetName,
  55. })
  56. if err != nil {
  57. return fmt.Errorf("error getting latest app revision: %s", err)
  58. }
  59. buildSettings, err := client.GetBuildFromRevision(ctx, api.GetBuildFromRevisionInput{
  60. ProjectID: cliConf.Project,
  61. ClusterID: cliConf.Cluster,
  62. AppName: inp.AppName,
  63. AppRevisionID: latest.AppRevision.ID,
  64. PatchOperations: inp.PatchOperations,
  65. })
  66. if err != nil {
  67. return fmt.Errorf("error getting build from revision: %w", err)
  68. }
  69. tagForBuild, err := tagFromCommitSHAOrFlag(inp.ImageTag)
  70. if err != nil {
  71. return fmt.Errorf("error getting tag for build: %w", err)
  72. }
  73. buildEnvVariables := make(map[string]string)
  74. for k, v := range buildSettings.BuildEnvVariables {
  75. buildEnvVariables[k] = v
  76. }
  77. // use all env variables from running container in build
  78. env := os.Environ()
  79. for _, v := range env {
  80. pair := strings.SplitN(v, "=", 2)
  81. if len(pair) == 2 {
  82. if strings.HasPrefix(pair[0], "PORTER_") || strings.HasPrefix(pair[0], "NEXT_PUBLIC_") {
  83. buildEnvVariables[pair[0]] = pair[1]
  84. }
  85. }
  86. }
  87. buildInput, err := buildInputFromBuildSettings(buildInputFromBuildSettingsInput{
  88. projectID: cliConf.Project,
  89. appName: inp.AppName,
  90. commitSHA: tagForBuild,
  91. image: buildSettings.Image,
  92. build: buildSettings.Build,
  93. buildEnv: buildEnvVariables,
  94. pullImageBeforeBuild: inp.PullImageBeforeBuild,
  95. })
  96. if err != nil {
  97. return fmt.Errorf("error creating build input from build settings: %w", err)
  98. }
  99. // skip push when only a build is requested
  100. buildInput.SkipPush = true
  101. buildOutput := build(ctx, client, buildInput)
  102. if buildOutput.Error != nil {
  103. return fmt.Errorf("error building app: %w", buildOutput.Error)
  104. }
  105. color.New(color.FgGreen).Printf("Successfully built image (tag: %s)\n", tagForBuild) // nolint:errcheck,gosec
  106. return nil
  107. }
  108. func tagFromCommitSHAOrFlag(providedTag string) (string, error) {
  109. tag := commitSHAFromEnv()
  110. if providedTag != "" {
  111. tag = providedTag
  112. }
  113. if tag == "" {
  114. return tag, errors.New("no tag set and could not determine latest commit SHA")
  115. }
  116. return tag, nil
  117. }