Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: add --diff flag to pipeline deploy #4991

Merged
merged 13 commits into from
Jun 19, 2023
5 changes: 5 additions & 0 deletions internal/pkg/cli/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,7 @@ type pipelineDeployer interface {
PipelineExists(env *deploy.CreatePipelineInput) (bool, error)
DeletePipeline(pipeline deploy.Pipeline) error
AddPipelineResourcesToApp(app *config.Application, region string) error
Template(stackName string) (string, error)
appResourcesGetter
// TODO: Add StreamPipelineCreation method
}
Expand Down Expand Up @@ -703,3 +704,7 @@ type envPackager interface {
AddonsTemplate() (string, error)
templateDiffer
}

type pipelineStackConfig interface {
Template() (string, error)
}
53 changes: 53 additions & 0 deletions internal/pkg/cli/mocks/mock_interfaces.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

96 changes: 79 additions & 17 deletions internal/pkg/cli/pipeline_deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ import (
"io"
"os"
"path/filepath"
"strings"

"github.com/aws/aws-sdk-go/service/ssm"
"github.com/spf13/afero"

"github.com/aws/copilot-cli/internal/pkg/aws/cloudformation"
awscloudformation "github.com/aws/copilot-cli/internal/pkg/aws/cloudformation"
cs "github.com/aws/copilot-cli/internal/pkg/aws/codestar"
"github.com/aws/copilot-cli/internal/pkg/aws/identity"
rg "github.com/aws/copilot-cli/internal/pkg/aws/resourcegroups"
Expand All @@ -24,7 +26,9 @@ import (
"github.com/aws/copilot-cli/internal/pkg/config"
"github.com/aws/copilot-cli/internal/pkg/deploy"
deploycfn "github.com/aws/copilot-cli/internal/pkg/deploy/cloudformation"
"github.com/aws/copilot-cli/internal/pkg/deploy/cloudformation/stack"
"github.com/aws/copilot-cli/internal/pkg/manifest"
templatediff "github.com/aws/copilot-cli/internal/pkg/template/diff"
"github.com/aws/copilot-cli/internal/pkg/term/color"
"github.com/aws/copilot-cli/internal/pkg/term/log"
"github.com/aws/copilot-cli/internal/pkg/term/prompt"
Expand Down Expand Up @@ -62,21 +66,25 @@ type deployPipelineVars struct {
appName string
name string
skipConfirmation bool
showDiff bool
}

type deployPipelineOpts struct {
deployPipelineVars

pipelineDeployer pipelineDeployer
sel wsPipelineSelector
prog progress
prompt prompter
region string
store store
ws wsPipelineReader
codestar codestar
newSvcListCmd func(io.Writer, string) cmd
newJobListCmd func(io.Writer, string) cmd
pipelineDeployer pipelineDeployer
sel wsPipelineSelector
prog progress
prompt prompter
region string
store store
ws wsPipelineReader
codestar codestar
diffWriter io.Writer
newSvcListCmd func(io.Writer, string) cmd
newJobListCmd func(io.Writer, string) cmd
pipelineStackConfig func(in *deploy.CreatePipelineInput) pipelineStackConfig

configureDeployedPipelineLister func() deployedPipelineLister

// cached variables
Expand Down Expand Up @@ -115,8 +123,12 @@ func newDeployPipelineOpts(vars deployPipelineVars) (*deployPipelineOpts, error)
store: store,
prog: termprogress.NewSpinner(log.DiagnosticWriter),
prompt: prompter,
diffWriter: os.Stdout,
sel: selector.NewWsPipelineSelector(prompter, ws),
codestar: cs.New(defaultSession),
pipelineStackConfig: func(in *deploy.CreatePipelineInput) pipelineStackConfig {
return stack.NewPipelineStackConfig(in)
},
newSvcListCmd: func(w io.Writer, appName string) cmd {
return &listSvcOpts{
listWkldVars: listWkldVars{
Expand Down Expand Up @@ -258,13 +270,59 @@ func (o *deployPipelineOpts) Execute() error {
PermissionsBoundary: o.app.PermissionsBoundary,
}

if o.showDiff {
tpl, err := o.pipelineStackConfig(deployPipelineInput).Template()
if err != nil {
return fmt.Errorf("generate the new template for diff: %w", err)
}
if err = diff(o, tpl, o.diffWriter); err != nil {
var errHasDiff *errHasDiff
if !errors.As(err, &errHasDiff) {
return err
}
}
if !o.skipConfirmation {
contd, err := o.prompt.Confirm(continueDeploymentPrompt, "")
if err != nil {
return fmt.Errorf("ask whether to continue with the deployment: %w", err)
}
if !contd {
return nil
}
}
}
if err := o.deployPipeline(deployPipelineInput); err != nil {
return err
}

return nil
}

// DeployDiff returns the stringified diff of the template against the deployed template of the pipeline.
func (o *deployPipelineOpts) DeployDiff(template string) (string, error) {
isLegacy, err := o.isLegacy(o.pipeline.Name)
if err != nil {
return "", err
}

tmpl, err := o.pipelineDeployer.Template(stack.NameForPipeline(o.app.Name, o.pipeline.Name, isLegacy))
if err != nil {
var errNotFound *awscloudformation.ErrStackNotFound
if !errors.As(err, &errNotFound) {
return "", fmt.Errorf("retrieve the deployed template for %q: %w", o.pipeline.Name, err)
}
tmpl = ""
}
diffTree, err := templatediff.From(tmpl).ParseWithCFNOverriders([]byte(template))
if err != nil {
return "", fmt.Errorf("parse the diff against the deployed pipeline stack %q: %w", o.pipeline.Name, err)
}
buf := strings.Builder{}
if err := diffTree.Write(&buf); err != nil {
return "", err
}
return buf.String(), nil
}

func (o *deployPipelineOpts) isLegacy(inputName string) (bool, error) {
lister := o.configureDeployedPipelineLister()
pipelines, err := lister.ListDeployedPipelines(o.appName)
Expand Down Expand Up @@ -446,13 +504,16 @@ func (o *deployPipelineOpts) deployPipeline(in *deploy.CreatePipelineInput) erro
}

// If the stack already exists - we update it
shouldUpdate, err := o.shouldUpdate()
if err != nil {
return err
}
if !shouldUpdate {
return nil
if !o.showDiff {
shouldUpdate, err := o.shouldUpdate()
if err != nil {
return err
}
if !shouldUpdate {
return nil
}
Varun359 marked this conversation as resolved.
Show resolved Hide resolved
}

o.prog.Start(fmt.Sprintf(fmtPipelineDeployProposalStart, color.HighlightUserInput(o.pipeline.Name)))
if err := o.pipelineDeployer.UpdatePipeline(in, bucketName); err != nil {
o.prog.Stop(log.Serrorf(fmtPipelineDeployProposalFailed, color.HighlightUserInput(o.pipeline.Name)))
Expand Down Expand Up @@ -493,5 +554,6 @@ func buildPipelineDeployCmd() *cobra.Command {
cmd.Flags().StringVarP(&vars.appName, appFlag, appFlagShort, "", appFlagDescription)
cmd.Flags().StringVarP(&vars.name, nameFlag, nameFlagShort, "", pipelineFlagDescription)
cmd.Flags().BoolVar(&vars.skipConfirmation, yesFlag, false, yesFlagDescription)
cmd.Flags().BoolVar(&vars.showDiff, diffFlag, false, diffFlagDescription)
return cmd
}
Loading