Explorar el Código

Merge pull request #1941 from porter-dev/nafees/cli-improvements

[POR-418] CLI improvements
abelanger5 hace 4 años
padre
commit
05178f4cdd

+ 3 - 5
cli/cmd/apply.go

@@ -175,8 +175,6 @@ type ApplicationConfig struct {
 	OnlyCreate bool
 	OnlyCreate bool
 
 
 	Build struct {
 	Build struct {
-		ForceBuild bool `mapstructure:"force_build"`
-		ForcePush  bool `mapstructure:"force_push"`
 		UseCache   bool `mapstructure:"use_cache"`
 		UseCache   bool `mapstructure:"use_cache"`
 		Method     string
 		Method     string
 		Context    string
 		Context    string
@@ -502,7 +500,7 @@ func (d *Driver) createApplication(resource *models.Resource, client *api.Client
 			}
 			}
 		}
 		}
 
 
-		subdomain, err = createAgent.CreateFromDocker(appConf.Values, sharedOpts.OverrideTag, buildConfig, appConf.Build.ForceBuild)
+		subdomain, err = createAgent.CreateFromDocker(appConf.Values, sharedOpts.OverrideTag, buildConfig)
 	}
 	}
 
 
 	if err != nil {
 	if err != nil {
@@ -551,14 +549,14 @@ func (d *Driver) updateApplication(resource *models.Resource, client *api.Client
 			}
 			}
 		}
 		}
 
 
-		err = updateAgent.Build(buildConfig, appConf.Build.ForceBuild)
+		err = updateAgent.Build(buildConfig)
 
 
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
 
 
 		if !appConf.Build.UseCache {
 		if !appConf.Build.UseCache {
-			err = updateAgent.Push(appConf.Build.ForcePush)
+			err = updateAgent.Push()
 
 
 			if err != nil {
 			if err != nil {
 				return nil, err
 				return nil, err

+ 3 - 1
cli/cmd/create.go

@@ -174,6 +174,8 @@ func init() {
 		false,
 		false,
 		"Whether to use cache (currently in beta)",
 		"Whether to use cache (currently in beta)",
 	)
 	)
+
+	createCmd.PersistentFlags().MarkDeprecated("force-build", "--force-build is deprecated")
 }
 }
 
 
 var supportedKinds = map[string]string{"web": "", "job": "", "worker": ""}
 var supportedKinds = map[string]string{"web": "", "job": "", "worker": ""}
@@ -276,7 +278,7 @@ func createFull(_ *types.GetAuthenticatedUserResponse, client *api.Client, args
 			}
 			}
 		}
 		}
 
 
-		subdomain, err := createAgent.CreateFromDocker(valuesObj, "default", nil, forceBuild)
+		subdomain, err := createAgent.CreateFromDocker(valuesObj, "default", nil)
 
 
 		return handleSubdomainCreate(subdomain, err)
 		return handleSubdomainCreate(subdomain, err)
 	} else if source == "github" {
 	} else if source == "github" {

+ 146 - 0
cli/cmd/delete.go

@@ -39,7 +39,63 @@ deleting a configuration:
 	},
 	},
 }
 }
 
 
+// deleteAppsCmd represents the "porter delete apps" subcommand
+var deleteAppsCmd = &cobra.Command{
+	Use:     "apps",
+	Aliases: []string{"app", "applications", "application"},
+	Short:   "Deletes an existing app",
+	Args:    cobra.ExactArgs(1),
+	Run: func(cmd *cobra.Command, args []string) {
+		err := checkLoginAndRun(args, deleteApp)
+
+		if err != nil {
+			os.Exit(1)
+		}
+	},
+}
+
+// deleteJobsCmd represents the "porter delete jobs" subcommand
+var deleteJobsCmd = &cobra.Command{
+	Use:     "jobs",
+	Aliases: []string{"job"},
+	Short:   "Deletes an existing job",
+	Args:    cobra.ExactArgs(1),
+	Run: func(cmd *cobra.Command, args []string) {
+		err := checkLoginAndRun(args, deleteJob)
+
+		if err != nil {
+			os.Exit(1)
+		}
+	},
+}
+
+// deleteAddonsCmd represents the "porter delete addons" subcommand
+var deleteAddonsCmd = &cobra.Command{
+	Use:     "addons",
+	Aliases: []string{"addon"},
+	Short:   "Deletes an existing addon",
+	Args:    cobra.ExactArgs(1),
+	Run: func(cmd *cobra.Command, args []string) {
+		err := checkLoginAndRun(args, deleteAddon)
+
+		if err != nil {
+			os.Exit(1)
+		}
+	},
+}
+
 func init() {
 func init() {
+	deleteCmd.PersistentFlags().StringVar(
+		&namespace,
+		"namespace",
+		"default",
+		"Namespace of the application",
+	)
+
+	deleteCmd.AddCommand(deleteAppsCmd)
+	deleteCmd.AddCommand(deleteJobsCmd)
+	deleteCmd.AddCommand(deleteAddonsCmd)
+
 	rootCmd.AddCommand(deleteCmd)
 	rootCmd.AddCommand(deleteCmd)
 }
 }
 
 
@@ -90,3 +146,93 @@ func delete(_ *types.GetAuthenticatedUserResponse, client *api.Client, args []st
 		gitRepoOwner, gitRepoName, gitPRNumber,
 		gitRepoOwner, gitRepoName, gitPRNumber,
 	)
 	)
 }
 }
+
+func deleteApp(_ *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
+	name := args[0]
+
+	resp, err := client.GetRelease(
+		context.Background(), cliConf.Project, cliConf.Cluster, namespace, name,
+	)
+
+	if err != nil {
+		return err
+	}
+
+	rel := *resp
+
+	if rel.Chart.Name() != "web" && rel.Chart.Name() != "worker" {
+		return fmt.Errorf("no app found with name: %s", name)
+	}
+
+	color.New(color.FgBlue).Printf("Deleting app: %s\n", name)
+
+	err = client.DeleteRelease(
+		context.Background(), cliConf.Project, cliConf.Cluster, namespace, name,
+	)
+
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func deleteJob(_ *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
+	name := args[0]
+
+	resp, err := client.GetRelease(
+		context.Background(), cliConf.Project, cliConf.Cluster, namespace, name,
+	)
+
+	if err != nil {
+		return err
+	}
+
+	rel := *resp
+
+	if rel.Chart.Name() != "job" {
+		return fmt.Errorf("no job found with name: %s", name)
+	}
+
+	color.New(color.FgBlue).Printf("Deleting job: %s\n", name)
+
+	err = client.DeleteRelease(
+		context.Background(), cliConf.Project, cliConf.Cluster, namespace, name,
+	)
+
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func deleteAddon(_ *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
+	name := args[0]
+
+	resp, err := client.GetRelease(
+		context.Background(), cliConf.Project, cliConf.Cluster, namespace, name,
+	)
+
+	if err != nil {
+		return err
+	}
+
+	rel := *resp
+
+	if rel.Chart.Name() != "web" && rel.Chart.Name() != "worker" && rel.Chart.Name() != "job" {
+		return fmt.Errorf("no addon found with name: %s", name)
+	}
+
+	color.New(color.FgBlue).Printf("Deleting job: %s\n", name)
+
+	err = client.DeleteRelease(
+		context.Background(), cliConf.Project, cliConf.Cluster, namespace, name,
+	)
+
+	if err != nil {
+		return err
+	}
+
+	return nil
+}

+ 44 - 7
cli/cmd/deploy.go

@@ -316,6 +316,10 @@ func init() {
 		"set this to force push an image (images tagged with \"latest\" have this set by default)",
 		"set this to force push an image (images tagged with \"latest\" have this set by default)",
 	)
 	)
 
 
+	updateCmd.PersistentFlags().MarkDeprecated("force-build", "--force-build is now deprecated")
+
+	updateCmd.PersistentFlags().MarkDeprecated("force-push", "--force-push is now deprecated")
+
 	updateCmd.AddCommand(updateGetEnvCmd)
 	updateCmd.AddCommand(updateGetEnvCmd)
 
 
 	updateGetEnvCmd.PersistentFlags().StringVar(
 	updateGetEnvCmd.PersistentFlags().StringVar(
@@ -532,7 +536,7 @@ func updateBuildWithAgent(updateAgent *deploy.DeployAgent) error {
 		return err
 		return err
 	}
 	}
 
 
-	if err := updateAgent.Build(nil, forceBuild); err != nil {
+	if err := updateAgent.Build(nil); err != nil {
 		if stream {
 		if stream {
 			updateAgent.StreamEvent(types.SubEvent{
 			updateAgent.StreamEvent(types.SubEvent{
 				EventID: "build",
 				EventID: "build",
@@ -578,7 +582,7 @@ func updatePushWithAgent(updateAgent *deploy.DeployAgent) error {
 		})
 		})
 	}
 	}
 
 
-	if err := updateAgent.Push(forcePush); err != nil {
+	if err := updateAgent.Push(); err != nil {
 		if stream {
 		if stream {
 			updateAgent.StreamEvent(types.SubEvent{
 			updateAgent.StreamEvent(types.SubEvent{
 				EventID: "push",
 				EventID: "push",
@@ -639,15 +643,48 @@ func updateUpgradeWithAgent(updateAgent *deploy.DeployAgent) error {
 		return err
 		return err
 	}
 	}
 
 
-	env, err := updateAgent.GetBuildEnv(&deploy.GetBuildEnvOpts{
-		UseNewConfig: false,
-	})
+	if len(updateAgent.Opts.AdditionalEnv) > 0 {
+		syncedEnv, err := deploy.GetSyncedEnv(
+			updateAgent.Client,
+			updateAgent.Release.Config,
+			updateAgent.Opts.ProjectID,
+			updateAgent.Opts.ClusterID,
+			updateAgent.Opts.Namespace,
+			false,
+		)
+
+		if err != nil {
+			return err
+		}
+
+		for k := range updateAgent.Opts.AdditionalEnv {
+			if _, ok := syncedEnv[k]; ok {
+				return fmt.Errorf("environment variable %s already exists as part of a synced environment group", k)
+			}
+		}
+
+		normalEnv, err := deploy.GetNormalEnv(
+			updateAgent.Client,
+			updateAgent.Release.Config,
+			updateAgent.Opts.ProjectID,
+			updateAgent.Opts.ClusterID,
+			updateAgent.Opts.Namespace,
+			false,
+		)
+
+		if err != nil {
+			return err
+		}
+
+		// add the additional environment variables to container.env.normal
+		for k, v := range updateAgent.Opts.AdditionalEnv {
+			normalEnv[k] = v
+		}
 
 
-	if err == nil && len(env) > 0 {
 		valuesObj = templaterUtils.CoalesceValues(valuesObj, map[string]interface{}{
 		valuesObj = templaterUtils.CoalesceValues(valuesObj, map[string]interface{}{
 			"container": map[string]interface{}{
 			"container": map[string]interface{}{
 				"env": map[string]interface{}{
 				"env": map[string]interface{}{
-					"normal": env,
+					"normal": normalEnv,
 				},
 				},
 			},
 			},
 		})
 		})

+ 51 - 58
cli/cmd/deploy/create.go

@@ -219,7 +219,6 @@ func (c *CreateAgent) CreateFromDocker(
 	overrideValues map[string]interface{},
 	overrideValues map[string]interface{},
 	imageTag string,
 	imageTag string,
 	extraBuildConfig *types.BuildConfig,
 	extraBuildConfig *types.BuildConfig,
-	forceBuild bool,
 ) (string, error) {
 ) (string, error) {
 	opts := c.CreateOpts
 	opts := c.CreateOpts
 
 
@@ -273,84 +272,78 @@ func (c *CreateAgent) CreateFromDocker(
 		return "", err
 		return "", err
 	}
 	}
 
 
-	imageExists := agent.CheckIfImageExists(imageURL, imageTag)
+	env, err := GetEnvForRelease(c.Client, mergedValues, opts.ProjectID, opts.ClusterID, opts.Namespace)
 
 
-	if imageExists && imageTag != "latest" && !forceBuild {
-		fmt.Printf("%s:%s already exists in the registry, so skipping build\n", imageURL, imageTag)
-	} else { // image does not exist or has tag "latest" so we (re)build one
-		env, err := GetEnvForRelease(c.Client, mergedValues, opts.ProjectID, opts.ClusterID, opts.Namespace)
-
-		if err != nil {
-			env = map[string]string{}
-		}
+	if err != nil {
+		env = make(map[string]string)
+	}
 
 
-		envConfig, err := GetNestedMap(mergedValues, "container", "env")
+	envConfig, err := GetNestedMap(mergedValues, "container", "env")
 
 
-		if err == nil {
-			_, exists := envConfig["build"]
+	if err == nil {
+		_, exists := envConfig["build"]
 
 
-			if exists {
-				buildEnv, err := GetNestedMap(mergedValues, "container", "env", "build")
+		if exists {
+			buildEnv, err := GetNestedMap(mergedValues, "container", "env", "build")
 
 
-				if err == nil {
-					for key, val := range buildEnv {
-						if valStr, ok := val.(string); ok {
-							env[key] = valStr
-						}
+			if err == nil {
+				for key, val := range buildEnv {
+					if valStr, ok := val.(string); ok {
+						env[key] = valStr
 					}
 					}
 				}
 				}
 			}
 			}
 		}
 		}
+	}
 
 
-		// add additional env based on options
-		for key, val := range opts.SharedOpts.AdditionalEnv {
-			env[key] = val
-		}
+	// add additional env based on options
+	for key, val := range opts.SharedOpts.AdditionalEnv {
+		env[key] = val
+	}
+
+	buildAgent := &BuildAgent{
+		SharedOpts:  opts.SharedOpts,
+		APIClient:   c.Client,
+		ImageRepo:   imageURL,
+		Env:         env,
+		ImageExists: false,
+	}
 
 
-		buildAgent := &BuildAgent{
-			SharedOpts:  opts.SharedOpts,
-			APIClient:   c.Client,
-			ImageRepo:   imageURL,
-			Env:         env,
-			ImageExists: false,
+	if opts.Method == DeployBuildTypeDocker {
+		basePath, err := filepath.Abs(".")
+
+		if err != nil {
+			return "", err
 		}
 		}
 
 
-		if opts.Method == DeployBuildTypeDocker {
-			basePath, err := filepath.Abs(".")
+		err = buildAgent.BuildDocker(agent, basePath, opts.LocalPath, opts.LocalDockerfile, imageTag, "")
+	} else {
+		err = buildAgent.BuildPack(agent, opts.LocalPath, imageTag, "", extraBuildConfig)
+	}
 
 
-			if err != nil {
-				return "", err
-			}
+	if err != nil {
+		return "", err
+	}
 
 
-			err = buildAgent.BuildDocker(agent, basePath, opts.LocalPath, opts.LocalDockerfile, imageTag, "")
-		} else {
-			err = buildAgent.BuildPack(agent, opts.LocalPath, imageTag, "", extraBuildConfig)
-		}
+	if !opts.SharedOpts.UseCache {
+		// create repository
+		err = c.Client.CreateRepository(
+			context.Background(),
+			opts.ProjectID,
+			regID,
+			&types.CreateRegistryRepositoryRequest{
+				ImageRepoURI: imageURL,
+			},
+		)
 
 
 		if err != nil {
 		if err != nil {
 			return "", err
 			return "", err
 		}
 		}
 
 
-		if !opts.SharedOpts.UseCache {
-			// create repository
-			err = c.Client.CreateRepository(
-				context.Background(),
-				opts.ProjectID,
-				regID,
-				&types.CreateRegistryRepositoryRequest{
-					ImageRepoURI: imageURL,
-				},
-			)
-
-			if err != nil {
-				return "", err
-			}
-
-			err = agent.PushImage(fmt.Sprintf("%s:%s", imageURL, imageTag))
+		err = agent.PushImage(fmt.Sprintf("%s:%s", imageURL, imageTag))
 
 
-			if err != nil {
-				return "", err
-			}
+		if err != nil {
+			return "", err
 		}
 		}
 	}
 	}
 
 

+ 112 - 65
cli/cmd/deploy/deploy.go

@@ -33,9 +33,9 @@ type DeployAgent struct {
 	App string
 	App string
 
 
 	Client         *client.Client
 	Client         *client.Client
-	release        *types.GetReleaseResponse
+	Opts           *DeployOpts
+	Release        *types.GetReleaseResponse
 	agent          *docker.Agent
 	agent          *docker.Agent
-	opts           *DeployOpts
 	tag            string
 	tag            string
 	envPrefix      string
 	envPrefix      string
 	env            map[string]string
 	env            map[string]string
@@ -56,7 +56,7 @@ type DeployOpts struct {
 func NewDeployAgent(client *client.Client, app string, opts *DeployOpts) (*DeployAgent, error) {
 func NewDeployAgent(client *client.Client, app string, opts *DeployOpts) (*DeployAgent, error) {
 	deployAgent := &DeployAgent{
 	deployAgent := &DeployAgent{
 		App:    app,
 		App:    app,
-		opts:   opts,
+		Opts:   opts,
 		Client: client,
 		Client: client,
 		env:    make(map[string]string),
 		env:    make(map[string]string),
 	}
 	}
@@ -68,7 +68,7 @@ func NewDeployAgent(client *client.Client, app string, opts *DeployOpts) (*Deplo
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	deployAgent.release = release
+	deployAgent.Release = release
 
 
 	// set an environment prefix to avoid collisions
 	// set an environment prefix to avoid collisions
 	deployAgent.envPrefix = fmt.Sprintf("PORTER_%s", strings.Replace(
 	deployAgent.envPrefix = fmt.Sprintf("PORTER_%s", strings.Replace(
@@ -90,27 +90,27 @@ func NewDeployAgent(client *client.Client, app string, opts *DeployOpts) (*Deplo
 			// if the git action config exists, and dockerfile path is not empty, build type
 			// if the git action config exists, and dockerfile path is not empty, build type
 			// is docker
 			// is docker
 			if release.GitActionConfig.DockerfilePath != "" {
 			if release.GitActionConfig.DockerfilePath != "" {
-				deployAgent.opts.Method = DeployBuildTypeDocker
+				deployAgent.Opts.Method = DeployBuildTypeDocker
 			} else {
 			} else {
 				// otherwise build type is pack
 				// otherwise build type is pack
-				deployAgent.opts.Method = DeployBuildTypePack
+				deployAgent.Opts.Method = DeployBuildTypePack
 			}
 			}
 		} else {
 		} else {
 			// if the git action config does not exist, we use docker by default
 			// if the git action config does not exist, we use docker by default
-			deployAgent.opts.Method = DeployBuildTypeDocker
+			deployAgent.Opts.Method = DeployBuildTypeDocker
 		}
 		}
 	}
 	}
 
 
-	if deployAgent.opts.Method == DeployBuildTypeDocker {
+	if deployAgent.Opts.Method == DeployBuildTypeDocker {
 		if release.GitActionConfig != nil {
 		if release.GitActionConfig != nil {
 			deployAgent.dockerfilePath = release.GitActionConfig.DockerfilePath
 			deployAgent.dockerfilePath = release.GitActionConfig.DockerfilePath
 		}
 		}
 
 
-		if deployAgent.opts.LocalDockerfile != "" {
-			deployAgent.dockerfilePath = deployAgent.opts.LocalDockerfile
+		if deployAgent.Opts.LocalDockerfile != "" {
+			deployAgent.dockerfilePath = deployAgent.Opts.LocalDockerfile
 		}
 		}
 
 
-		if deployAgent.dockerfilePath == "" && deployAgent.opts.LocalDockerfile == "" {
+		if deployAgent.dockerfilePath == "" && deployAgent.Opts.LocalDockerfile == "" {
 			deployAgent.dockerfilePath = "./Dockerfile"
 			deployAgent.dockerfilePath = "./Dockerfile"
 		}
 		}
 	}
 	}
@@ -119,7 +119,7 @@ func NewDeployAgent(client *client.Client, app string, opts *DeployOpts) (*Deplo
 	// will fail. we set the image based on the git action config or the image written in the
 	// will fail. we set the image based on the git action config or the image written in the
 	// helm values
 	// helm values
 	if release.GitActionConfig == nil {
 	if release.GitActionConfig == nil {
-		deployAgent.opts.Local = true
+		deployAgent.Opts.Local = true
 
 
 		imageRepo, err := deployAgent.getReleaseImage()
 		imageRepo, err := deployAgent.getReleaseImage()
 
 
@@ -129,16 +129,16 @@ func NewDeployAgent(client *client.Client, app string, opts *DeployOpts) (*Deplo
 
 
 		deployAgent.imageRepo = imageRepo
 		deployAgent.imageRepo = imageRepo
 
 
-		deployAgent.dockerfilePath = deployAgent.opts.LocalDockerfile
+		deployAgent.dockerfilePath = deployAgent.Opts.LocalDockerfile
 	} else {
 	} else {
 		deployAgent.imageRepo = release.GitActionConfig.ImageRepoURI
 		deployAgent.imageRepo = release.GitActionConfig.ImageRepoURI
-		deployAgent.opts.LocalPath = release.GitActionConfig.FolderPath
+		deployAgent.Opts.LocalPath = release.GitActionConfig.FolderPath
 	}
 	}
 
 
 	deployAgent.tag = opts.OverrideTag
 	deployAgent.tag = opts.OverrideTag
 
 
-	err = coalesceEnvGroups(deployAgent.Client, deployAgent.opts.ProjectID, deployAgent.opts.ClusterID,
-		deployAgent.opts.Namespace, deployAgent.opts.EnvGroups, deployAgent.release.Config)
+	err = coalesceEnvGroups(deployAgent.Client, deployAgent.Opts.ProjectID, deployAgent.Opts.ClusterID,
+		deployAgent.Opts.Namespace, deployAgent.Opts.EnvGroups, deployAgent.Release.Config)
 
 
 	deployAgent.imageExists = deployAgent.agent.CheckIfImageExists(deployAgent.imageRepo, deployAgent.tag)
 	deployAgent.imageExists = deployAgent.agent.CheckIfImageExists(deployAgent.imageRepo, deployAgent.tag)
 
 
@@ -151,17 +151,23 @@ type GetBuildEnvOpts struct {
 	IncludeBuildEnv bool
 	IncludeBuildEnv bool
 }
 }
 
 
-// GetBuildEnv retrieves the build env from the release config and returns it
+// GetBuildEnv retrieves the build env from the release config and returns it.
+//
+// It returns a flattened map of all environment variables including:
+//    1. container.env.normal from the release config
+//    2. container.env.build from the release config
+//    3. container.env.synced from the release config
+//    4. any additional env var that was passed into the DeployAgent as opts.SharedOpts.AdditionalEnv
 func (d *DeployAgent) GetBuildEnv(opts *GetBuildEnvOpts) (map[string]string, error) {
 func (d *DeployAgent) GetBuildEnv(opts *GetBuildEnvOpts) (map[string]string, error) {
-	conf := d.release.Config
+	conf := d.Release.Config
 
 
 	if opts.UseNewConfig {
 	if opts.UseNewConfig {
 		if opts.NewConfig != nil {
 		if opts.NewConfig != nil {
-			conf = utils.CoalesceValues(d.release.Config, opts.NewConfig)
+			conf = utils.CoalesceValues(d.Release.Config, opts.NewConfig)
 		}
 		}
 	}
 	}
 
 
-	env, err := GetEnvForRelease(d.Client, conf, d.opts.ProjectID, d.opts.ClusterID, d.opts.Namespace)
+	env, err := GetEnvForRelease(d.Client, conf, d.Opts.ProjectID, d.Opts.ClusterID, d.Opts.Namespace)
 
 
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
@@ -186,7 +192,7 @@ func (d *DeployAgent) GetBuildEnv(opts *GetBuildEnvOpts) (map[string]string, err
 	}
 	}
 
 
 	// add additional env based on options
 	// add additional env based on options
-	for key, val := range d.opts.SharedOpts.AdditionalEnv {
+	for key, val := range d.Opts.SharedOpts.AdditionalEnv {
 		env[key] = val
 		env[key] = val
 	}
 	}
 
 
@@ -240,30 +246,23 @@ func (d *DeployAgent) WriteBuildEnv(fileDest string) error {
 
 
 // Build uses the deploy agent options to build a new container image from either
 // Build uses the deploy agent options to build a new container image from either
 // buildpack or docker.
 // buildpack or docker.
-func (d *DeployAgent) Build(overrideBuildConfig *types.BuildConfig, forceBuild bool) error {
+func (d *DeployAgent) Build(overrideBuildConfig *types.BuildConfig) error {
 	// retrieve current image to use for cache
 	// retrieve current image to use for cache
-	currImageSection := d.release.Config["image"].(map[string]interface{})
+	currImageSection := d.Release.Config["image"].(map[string]interface{})
 	currentTag := currImageSection["tag"].(string)
 	currentTag := currImageSection["tag"].(string)
 
 
 	if d.tag == "" {
 	if d.tag == "" {
 		d.tag = currentTag
 		d.tag = currentTag
 	}
 	}
 
 
-	// we do not want to re-build an image
-	// FIXME: what if overrideBuildConfig == nil but the image stays the same?
-	if overrideBuildConfig == nil && d.imageExists && d.tag != "latest" && !forceBuild {
-		fmt.Printf("%s:%s already exists in the registry, so skipping build\n", d.imageRepo, d.tag)
-		return nil
-	}
-
 	// if build is not local, fetch remote source
 	// if build is not local, fetch remote source
 	var basePath string
 	var basePath string
 	var err error
 	var err error
 
 
-	buildCtx := d.opts.LocalPath
+	buildCtx := d.Opts.LocalPath
 
 
-	if !d.opts.Local {
-		repoSplit := strings.Split(d.release.GitActionConfig.GitRepo, "/")
+	if !d.Opts.Local {
+		repoSplit := strings.Split(d.Release.GitActionConfig.GitRepo, "/")
 
 
 		if len(repoSplit) != 2 {
 		if len(repoSplit) != 2 {
 			return fmt.Errorf("invalid formatting of repo name")
 			return fmt.Errorf("invalid formatting of repo name")
@@ -271,12 +270,12 @@ func (d *DeployAgent) Build(overrideBuildConfig *types.BuildConfig, forceBuild b
 
 
 		zipResp, err := d.Client.GetRepoZIPDownloadURL(
 		zipResp, err := d.Client.GetRepoZIPDownloadURL(
 			context.Background(),
 			context.Background(),
-			d.opts.ProjectID,
-			int64(d.release.GitActionConfig.GitRepoID),
+			d.Opts.ProjectID,
+			int64(d.Release.GitActionConfig.GitRepoID),
 			"github",
 			"github",
 			repoSplit[0],
 			repoSplit[0],
 			repoSplit[1],
 			repoSplit[1],
-			d.release.GitActionConfig.GitBranch,
+			d.Release.GitActionConfig.GitBranch,
 		)
 		)
 
 
 		if err != nil {
 		if err != nil {
@@ -310,14 +309,14 @@ func (d *DeployAgent) Build(overrideBuildConfig *types.BuildConfig, forceBuild b
 	}
 	}
 
 
 	buildAgent := &BuildAgent{
 	buildAgent := &BuildAgent{
-		SharedOpts:  d.opts.SharedOpts,
+		SharedOpts:  d.Opts.SharedOpts,
 		APIClient:   d.Client,
 		APIClient:   d.Client,
 		ImageRepo:   d.imageRepo,
 		ImageRepo:   d.imageRepo,
 		Env:         d.env,
 		Env:         d.env,
 		ImageExists: d.imageExists,
 		ImageExists: d.imageExists,
 	}
 	}
 
 
-	if d.opts.Method == DeployBuildTypeDocker {
+	if d.Opts.Method == DeployBuildTypeDocker {
 		return buildAgent.BuildDocker(
 		return buildAgent.BuildDocker(
 			d.agent,
 			d.agent,
 			basePath,
 			basePath,
@@ -328,7 +327,7 @@ func (d *DeployAgent) Build(overrideBuildConfig *types.BuildConfig, forceBuild b
 		)
 		)
 	}
 	}
 
 
-	buildConfig := d.release.BuildConfig
+	buildConfig := d.Release.BuildConfig
 
 
 	if overrideBuildConfig != nil {
 	if overrideBuildConfig != nil {
 		buildConfig = overrideBuildConfig
 		buildConfig = overrideBuildConfig
@@ -338,12 +337,7 @@ func (d *DeployAgent) Build(overrideBuildConfig *types.BuildConfig, forceBuild b
 }
 }
 
 
 // Push pushes a local image to the remote repository linked in the release
 // Push pushes a local image to the remote repository linked in the release
-func (d *DeployAgent) Push(forcePush bool) error {
-	if d.imageExists && !forcePush && d.tag != "latest" {
-		fmt.Printf("%s:%s has been pushed already, so skipping push\n", d.imageRepo, d.tag)
-		return nil
-	}
-
+func (d *DeployAgent) Push() error {
 	return d.agent.PushImage(fmt.Sprintf("%s:%s", d.imageRepo, d.tag))
 	return d.agent.PushImage(fmt.Sprintf("%s:%s", d.imageRepo, d.tag))
 }
 }
 
 
@@ -354,11 +348,11 @@ func (d *DeployAgent) Push(forcePush bool) error {
 func (d *DeployAgent) UpdateImageAndValues(overrideValues map[string]interface{}) error {
 func (d *DeployAgent) UpdateImageAndValues(overrideValues map[string]interface{}) error {
 	// if this is a job chart, set "paused" to false so that the job doesn't run, unless
 	// if this is a job chart, set "paused" to false so that the job doesn't run, unless
 	// the user has explicitly overriden the "paused" field
 	// the user has explicitly overriden the "paused" field
-	if _, exists := overrideValues["paused"]; d.release.Chart.Name() == "job" && !exists {
+	if _, exists := overrideValues["paused"]; d.Release.Chart.Name() == "job" && !exists {
 		overrideValues["paused"] = true
 		overrideValues["paused"] = true
 	}
 	}
 
 
-	mergedValues := utils.CoalesceValues(d.release.Config, overrideValues)
+	mergedValues := utils.CoalesceValues(d.Release.Config, overrideValues)
 
 
 	activeBlueGreenTagVal := GetCurrActiveBlueGreenImage(mergedValues)
 	activeBlueGreenTagVal := GetCurrActiveBlueGreenImage(mergedValues)
 
 
@@ -404,10 +398,10 @@ func (d *DeployAgent) UpdateImageAndValues(overrideValues map[string]interface{}
 
 
 	return d.Client.UpgradeRelease(
 	return d.Client.UpgradeRelease(
 		context.Background(),
 		context.Background(),
-		d.opts.ProjectID,
-		d.opts.ClusterID,
-		d.release.Namespace,
-		d.release.Name,
+		d.Opts.ProjectID,
+		d.Opts.ClusterID,
+		d.Release.Namespace,
+		d.Release.Name,
 		&types.UpgradeReleaseRequest{
 		&types.UpgradeReleaseRequest{
 			Values: string(bytes),
 			Values: string(bytes),
 		},
 		},
@@ -426,11 +420,50 @@ type SyncedEnvSectionKey struct {
 }
 }
 
 
 // GetEnvForRelease gets the env vars for a standard Porter template config. These env
 // GetEnvForRelease gets the env vars for a standard Porter template config. These env
-// vars are found at `container.env.normal`.
-func GetEnvForRelease(client *client.Client, config map[string]interface{}, projID, clusterID uint, namespace string) (map[string]string, error) {
+// vars are found at `container.env.normal` and `container.env.synced`.
+func GetEnvForRelease(
+	client *client.Client,
+	config map[string]interface{},
+	projID, clusterID uint,
+	namespace string,
+) (map[string]string, error) {
 	res := make(map[string]string)
 	res := make(map[string]string)
 
 
 	// first, get the env vars from "container.env.normal"
 	// first, get the env vars from "container.env.normal"
+	normalEnv, err := GetNormalEnv(client, config, projID, clusterID, namespace, true)
+
+	if err != nil {
+		return nil, fmt.Errorf("error while fetching container.env.normal variables: %w", err)
+	}
+
+	for k, v := range normalEnv {
+		res[k] = v
+	}
+
+	// next, get the env vars specified by "container.env.synced"
+	// look for container.env.synced
+	syncedEnv, err := GetSyncedEnv(client, config, projID, clusterID, namespace, true)
+
+	if err != nil {
+		return nil, fmt.Errorf("error while fetching container.env.synced variables: %w", err)
+	}
+
+	for k, v := range syncedEnv {
+		res[k] = v
+	}
+
+	return res, nil
+}
+
+func GetNormalEnv(
+	client *client.Client,
+	config map[string]interface{},
+	projID, clusterID uint,
+	namespace string,
+	buildTime bool,
+) (map[string]string, error) {
+	res := make(map[string]string)
+
 	envConfig, err := GetNestedMap(config, "container", "env", "normal")
 	envConfig, err := GetNestedMap(config, "container", "env", "normal")
 
 
 	// if the field is not found, set envConfig to an empty map; this release has no env set
 	// if the field is not found, set envConfig to an empty map; this release has no env set
@@ -447,13 +480,25 @@ func GetEnvForRelease(client *client.Client, config map[string]interface{}, proj
 
 
 		// if the value contains PORTERSECRET, this is a "dummy" env that gets injected during
 		// if the value contains PORTERSECRET, this is a "dummy" env that gets injected during
 		// run-time, so we ignore it
 		// run-time, so we ignore it
-		if !strings.Contains(valStr, "PORTERSECRET") {
+		if buildTime && strings.Contains(valStr, "PORTERSECRET") {
+			continue
+		} else {
 			res[key] = valStr
 			res[key] = valStr
 		}
 		}
 	}
 	}
 
 
-	// next, get the env vars specified by "container.env.synced"
-	// look for container.env.synced
+	return res, nil
+}
+
+func GetSyncedEnv(
+	client *client.Client,
+	config map[string]interface{},
+	projID, clusterID uint,
+	namespace string,
+	buildTime bool,
+) (map[string]string, error) {
+	res := make(map[string]string)
+
 	envConf, err := GetNestedMap(config, "container", "env")
 	envConf, err := GetNestedMap(config, "container", "env")
 
 
 	// if error, just return the env detected from above
 	// if error, just return the env detected from above
@@ -561,7 +606,9 @@ func GetEnvForRelease(client *client.Client, config map[string]interface{}, proj
 			}
 			}
 
 
 			for key, val := range eg.Variables {
 			for key, val := range eg.Variables {
-				if !strings.Contains(val, "PORTERSECRET") {
+				if buildTime && strings.Contains(val, "PORTERSECRET") {
+					continue
+				} else {
 					res[key] = val
 					res[key] = val
 				}
 				}
 			}
 			}
@@ -572,12 +619,12 @@ func GetEnvForRelease(client *client.Client, config map[string]interface{}, proj
 }
 }
 
 
 func (d *DeployAgent) getReleaseImage() (string, error) {
 func (d *DeployAgent) getReleaseImage() (string, error) {
-	if d.release.ImageRepoURI != "" {
-		return d.release.ImageRepoURI, nil
+	if d.Release.ImageRepoURI != "" {
+		return d.Release.ImageRepoURI, nil
 	}
 	}
 
 
 	// get the image from the conig
 	// get the image from the conig
-	imageConfig, err := GetNestedMap(d.release.Config, "image")
+	imageConfig, err := GetNestedMap(d.Release.Config, "image")
 
 
 	if err != nil {
 	if err != nil {
 		return "", fmt.Errorf("could not get image config from release: %s", err.Error())
 		return "", fmt.Errorf("could not get image config from release: %s", err.Error())
@@ -600,7 +647,7 @@ func (d *DeployAgent) getReleaseImage() (string, error) {
 
 
 func (d *DeployAgent) pullCurrentReleaseImage() (string, error) {
 func (d *DeployAgent) pullCurrentReleaseImage() (string, error) {
 	// pull the currently deployed image to use cache, if possible
 	// pull the currently deployed image to use cache, if possible
-	imageConfig, err := GetNestedMap(d.release.Config, "image")
+	imageConfig, err := GetNestedMap(d.Release.Config, "image")
 
 
 	if err != nil {
 	if err != nil {
 		return "", fmt.Errorf("could not get image config from release: %s", err.Error())
 		return "", fmt.Errorf("could not get image config from release: %s", err.Error())
@@ -635,7 +682,7 @@ func (d *DeployAgent) downloadRepoToDir(downloadURL string) (string, error) {
 	downloader := &github.ZIPDownloader{
 	downloader := &github.ZIPDownloader{
 		ZipFolderDest:       dstDir,
 		ZipFolderDest:       dstDir,
 		AssetFolderDest:     dstDir,
 		AssetFolderDest:     dstDir,
-		ZipName:             fmt.Sprintf("%s.zip", strings.Replace(d.release.GitActionConfig.GitRepo, "/", "-", 1)),
+		ZipName:             fmt.Sprintf("%s.zip", strings.Replace(d.Release.GitActionConfig.GitRepo, "/", "-", 1)),
 		RemoveAfterDownload: true,
 		RemoveAfterDownload: true,
 	}
 	}
 
 
@@ -656,7 +703,7 @@ func (d *DeployAgent) downloadRepoToDir(downloadURL string) (string, error) {
 	dstFiles, err := ioutil.ReadDir(dstDir)
 	dstFiles, err := ioutil.ReadDir(dstDir)
 
 
 	for _, info := range dstFiles {
 	for _, info := range dstFiles {
-		if info.Mode().IsDir() && strings.Contains(info.Name(), strings.Replace(d.release.GitActionConfig.GitRepo, "/", "-", 1)) {
+		if info.Mode().IsDir() && strings.Contains(info.Name(), strings.Replace(d.Release.GitActionConfig.GitRepo, "/", "-", 1)) {
 			res = filepath.Join(dstDir, info.Name())
 			res = filepath.Join(dstDir, info.Name())
 		}
 		}
 	}
 	}
@@ -671,8 +718,8 @@ func (d *DeployAgent) downloadRepoToDir(downloadURL string) (string, error) {
 func (d *DeployAgent) StreamEvent(event types.SubEvent) error {
 func (d *DeployAgent) StreamEvent(event types.SubEvent) error {
 	return d.Client.CreateEvent(
 	return d.Client.CreateEvent(
 		context.Background(),
 		context.Background(),
-		d.opts.ProjectID, d.opts.ClusterID,
-		d.release.Namespace, d.release.Name,
+		d.Opts.ProjectID, d.Opts.ClusterID,
+		d.Release.Namespace, d.Release.Name,
 		&types.UpdateReleaseStepsRequest{
 		&types.UpdateReleaseStepsRequest{
 			Event: event,
 			Event: event,
 		},
 		},

+ 72 - 33
cli/cmd/list.go

@@ -6,18 +6,27 @@ import (
 	"os"
 	"os"
 	"text/tabwriter"
 	"text/tabwriter"
 
 
+	"github.com/fatih/color"
 	api "github.com/porter-dev/porter/api/client"
 	api "github.com/porter-dev/porter/api/client"
 	"github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/api/types"
 	"github.com/spf13/cobra"
 	"github.com/spf13/cobra"
-	"helm.sh/helm/v3/pkg/release"
 )
 )
 
 
-// listCmd represents the "porter list" base command when called
-// without any subcommands
+// listCmd represents the "porter list" base command and "porter list all" subcommand
 var listCmd = &cobra.Command{
 var listCmd = &cobra.Command{
 	Use:   "list",
 	Use:   "list",
-	Args:  cobra.ExactArgs(1),
-	Short: "List applications or jobs.",
+	Short: "List applications, addons or jobs.",
+	Run: func(cmd *cobra.Command, args []string) {
+		if len(args) == 0 || (args[0] == "all") {
+			err := checkLoginAndRun(args, listAll)
+
+			if err != nil {
+				os.Exit(1)
+			}
+		} else {
+			color.New(color.FgRed).Printf("invalid command: %s\n", args[0])
+		}
+	},
 }
 }
 
 
 var listAppsCmd = &cobra.Command{
 var listAppsCmd = &cobra.Command{
@@ -46,6 +55,19 @@ var listJobsCmd = &cobra.Command{
 	},
 	},
 }
 }
 
 
+var listAddonsCmd = &cobra.Command{
+	Use:     "addons",
+	Aliases: []string{"addon"},
+	Short:   "Lists addons in a specific namespace, or across all namespaces",
+	Run: func(cmd *cobra.Command, args []string) {
+		err := checkLoginAndRun(args, listAddons)
+
+		if err != nil {
+			os.Exit(1)
+		}
+	},
+}
+
 func init() {
 func init() {
 	listCmd.PersistentFlags().StringVar(
 	listCmd.PersistentFlags().StringVar(
 		&namespace,
 		&namespace,
@@ -56,37 +78,52 @@ func init() {
 
 
 	listCmd.AddCommand(listAppsCmd)
 	listCmd.AddCommand(listAppsCmd)
 	listCmd.AddCommand(listJobsCmd)
 	listCmd.AddCommand(listJobsCmd)
+	listCmd.AddCommand(listAddonsCmd)
 
 
 	rootCmd.AddCommand(listCmd)
 	rootCmd.AddCommand(listCmd)
 }
 }
 
 
-func listApps(_ *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
-	releases, err := client.ListReleases(context.Background(), cliConf.Project, cliConf.Cluster, namespace, &types.ListReleasesRequest{
-		ReleaseListFilter: &types.ReleaseListFilter{
-			Limit: 50,
-			Skip:  0,
-			StatusFilter: []string{
-				"deployed",
-				"uninstalled",
-				"pending",
-				"pending-install",
-				"pending-upgrade",
-				"pending-rollback",
-				"failed",
-			},
-		},
-	})
+func listAll(_ *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
+	err := writeReleases(client, "all")
 
 
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	writeReleases("application", releases)
+	return nil
+}
+
+func listApps(_ *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
+	err := writeReleases(client, "application")
+
+	if err != nil {
+		return err
+	}
 
 
 	return nil
 	return nil
 }
 }
 
 
 func listJobs(_ *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
 func listJobs(_ *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
+	err := writeReleases(client, "job")
+
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func listAddons(_ *types.GetAuthenticatedUserResponse, client *api.Client, args []string) error {
+	err := writeReleases(client, "addon")
+
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func writeReleases(client *api.Client, kind string) error {
 	releases, err := client.ListReleases(context.Background(), cliConf.Project, cliConf.Cluster, namespace, &types.ListReleasesRequest{
 	releases, err := client.ListReleases(context.Background(), cliConf.Project, cliConf.Cluster, namespace, &types.ListReleasesRequest{
 		ReleaseListFilter: &types.ReleaseListFilter{
 		ReleaseListFilter: &types.ReleaseListFilter{
 			Limit: 50,
 			Limit: 50,
@@ -107,24 +144,26 @@ func listJobs(_ *types.GetAuthenticatedUserResponse, client *api.Client, args []
 		return err
 		return err
 	}
 	}
 
 
-	writeReleases("job", releases)
-
-	return nil
-}
-
-func writeReleases(kind string, releases []*release.Release) {
 	w := new(tabwriter.Writer)
 	w := new(tabwriter.Writer)
 	w.Init(os.Stdout, 3, 8, 0, '\t', tabwriter.AlignRight)
 	w.Init(os.Stdout, 3, 8, 0, '\t', tabwriter.AlignRight)
 
 
-	fmt.Fprintf(w, "%s\t%s\t%s\n", "NAME", "NAMESPACE", "STATUS")
+	fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", "NAME", "NAMESPACE", "STATUS", "KIND")
 
 
 	for _, rel := range releases {
 	for _, rel := range releases {
-		if chartName := rel.Chart.Name(); kind == "application" && (chartName == "web" || chartName == "worker") {
-			fmt.Fprintf(w, "%s\t%s\t%s\n", rel.Name, rel.Namespace, rel.Info.Status)
-		} else if chartName := rel.Chart.Name(); kind == "job" && (chartName == "job") {
-			fmt.Fprintf(w, "%s\t%s\t%s\n", rel.Name, rel.Namespace, rel.Info.Status)
+		chartName := rel.Chart.Name()
+
+		if kind == "all" {
+			fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", rel.Name, rel.Namespace, rel.Info.Status, chartName)
+		} else if kind == "application" && (chartName == "web" || chartName == "worker") {
+			fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", rel.Name, rel.Namespace, rel.Info.Status, chartName)
+		} else if kind == "job" && chartName == "job" {
+			fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", rel.Name, rel.Namespace, rel.Info.Status, chartName)
+		} else if kind == "addon" && chartName != "web" && chartName != "worker" && chartName != "job" {
+			fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", rel.Name, rel.Namespace, rel.Info.Status, chartName)
 		}
 		}
 	}
 	}
 
 
 	w.Flush()
 	w.Flush()
+
+	return nil
 }
 }

+ 66 - 68
cli/cmd/preview/build_image_driver.go

@@ -20,7 +20,6 @@ import (
 
 
 type BuildDriverConfig struct {
 type BuildDriverConfig struct {
 	Build struct {
 	Build struct {
-		ForceBuild   bool `mapstructure:"force_build"`
 		UsePackCache bool `mapstructure:"use_pack_cache"`
 		UsePackCache bool `mapstructure:"use_pack_cache"`
 		Method       string
 		Method       string
 		Context      string
 		Context      string
@@ -28,6 +27,7 @@ type BuildDriverConfig struct {
 		Builder      string
 		Builder      string
 		Buildpacks   []string
 		Buildpacks   []string
 		Image        string
 		Image        string
+		Env          map[string]string
 	}
 	}
 
 
 	EnvGroups []types.EnvGroupMeta `mapstructure:"env_groups"`
 	EnvGroups []types.EnvGroupMeta `mapstructure:"env_groups"`
@@ -231,92 +231,90 @@ func (d *BuildDriver) Apply(resource *models.Resource) (*models.Resource, error)
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	imageExists := agent.CheckIfImageExists(imageURL, tag) // FIXME: does not seem to work with gcr.io images
+	_, mergedValues, err := createAgent.GetMergedValues(d.config.Values)
 
 
-	if imageExists && tag != "latest" && !d.config.Build.ForceBuild {
-		fmt.Printf("%s:%s already exists in the registry, so skipping build\n", imageURL, tag)
-	} else {
-		_, mergedValues, err := createAgent.GetMergedValues(d.config.Values)
-
-		if err != nil {
-			return nil, err
-		}
+	if err != nil {
+		return nil, err
+	}
 
 
-		env, err := deploy.GetEnvForRelease(
-			client,
-			mergedValues,
-			d.target.Project,
-			d.target.Cluster,
-			d.target.Namespace,
-		)
+	env, err := deploy.GetEnvForRelease(
+		client,
+		mergedValues,
+		d.target.Project,
+		d.target.Cluster,
+		d.target.Namespace,
+	)
 
 
-		if err != nil {
-			env = map[string]string{}
-		}
+	if err != nil {
+		env = make(map[string]string)
+	}
 
 
-		envConfig, err := deploy.GetNestedMap(mergedValues, "container", "env")
+	envConfig, err := deploy.GetNestedMap(mergedValues, "container", "env")
 
 
-		if err == nil {
-			_, exists := envConfig["build"]
+	if err == nil {
+		_, exists := envConfig["build"]
 
 
-			if exists {
-				buildEnv, err := deploy.GetNestedMap(mergedValues, "container", "env", "build")
+		if exists {
+			buildEnv, err := deploy.GetNestedMap(mergedValues, "container", "env", "build")
 
 
-				if err == nil {
-					for key, val := range buildEnv {
-						if valStr, ok := val.(string); ok {
-							env[key] = valStr
-						}
+			if err == nil {
+				for key, val := range buildEnv {
+					if valStr, ok := val.(string); ok {
+						env[key] = valStr
 					}
 					}
 				}
 				}
 			}
 			}
 		}
 		}
+	}
 
 
-		buildAgent := &deploy.BuildAgent{
-			SharedOpts:  createAgent.CreateOpts.SharedOpts,
-			APIClient:   client,
-			ImageRepo:   imageURL,
-			Env:         env,
-			ImageExists: false,
-		}
+	for k, v := range d.config.Build.Env {
+		env[k] = v
+	}
 
 
-		if d.config.Build.Method == string(deploy.DeployBuildTypeDocker) {
-			basePath, err := filepath.Abs(".")
+	buildAgent := &deploy.BuildAgent{
+		SharedOpts:  createAgent.CreateOpts.SharedOpts,
+		APIClient:   client,
+		ImageRepo:   imageURL,
+		Env:         env,
+		ImageExists: false,
+	}
 
 
-			if err != nil {
-				return nil, err
-			}
+	if d.config.Build.Method == string(deploy.DeployBuildTypeDocker) {
+		basePath, err := filepath.Abs(".")
 
 
-			err = buildAgent.BuildDocker(
-				agent,
-				basePath,
-				d.config.Build.Context,
-				d.config.Build.Dockerfile,
-				tag,
-				"",
-			)
-		} else {
-			var buildConfig *types.BuildConfig
+		if err != nil {
+			return nil, err
+		}
 
 
-			if d.config.Build.Builder != "" {
-				buildConfig = &types.BuildConfig{
-					Builder:    d.config.Build.Builder,
-					Buildpacks: d.config.Build.Buildpacks,
-				}
-			}
+		err = buildAgent.BuildDocker(
+			agent,
+			basePath,
+			d.config.Build.Context,
+			d.config.Build.Dockerfile,
+			tag,
+			"",
+		)
+	} else {
+		var buildConfig *types.BuildConfig
 
 
-			err = buildAgent.BuildPack(
-				agent,
-				d.config.Build.Context,
-				tag,
-				"",
-				buildConfig,
-			)
+		if d.config.Build.Builder != "" {
+			buildConfig = &types.BuildConfig{
+				Builder:    d.config.Build.Builder,
+				Buildpacks: d.config.Build.Buildpacks,
+			}
 		}
 		}
 
 
-		if err != nil {
-			return nil, err
-		}
+		err = buildAgent.BuildPack(
+			agent,
+			d.config.Build.Context,
+			tag,
+			"",
+			buildConfig,
+		)
+	}
+
+	if err != nil {
+		return nil, err
 	}
 	}
 
 
 	named, _ := reference.ParseNamed(imageURL)
 	named, _ := reference.ParseNamed(imageURL)

+ 0 - 1
cli/cmd/preview/push_image_driver.go

@@ -12,7 +12,6 @@ import (
 
 
 type PushDriverConfig struct {
 type PushDriverConfig struct {
 	Push struct {
 	Push struct {
-		ForcePush    bool `mapstructure:"force_push"`
 		UsePackCache bool `mapstructure:"use_pack_cache"`
 		UsePackCache bool `mapstructure:"use_pack_cache"`
 		Image        string
 		Image        string
 	}
 	}