github.com/apex/up@v1.7.1/internal/cli/deploy/deploy.go (about)

     1  package deploy
     2  
     3  import (
     4  	"os"
     5  	"time"
     6  
     7  	"github.com/pkg/errors"
     8  	"github.com/tj/go/git"
     9  	"github.com/tj/go/term"
    10  	"github.com/tj/kingpin"
    11  
    12  	"github.com/apex/up"
    13  	"github.com/apex/up/internal/cli/root"
    14  	"github.com/apex/up/internal/setup"
    15  	"github.com/apex/up/internal/stats"
    16  	"github.com/apex/up/internal/util"
    17  	"github.com/apex/up/internal/validate"
    18  )
    19  
    20  func init() {
    21  	cmd := root.Command("deploy", "Deploy the project.").Default()
    22  	stage := cmd.Arg("stage", "Target stage name.").Default("staging").String()
    23  	noBuild := cmd.Flag("no-build", "Disable build related hooks.").Bool()
    24  
    25  	cmd.Example(`up deploy`, "Deploy to the staging environment.")
    26  	cmd.Example(`up deploy production`, "Deploy to the production environment.")
    27  	cmd.Example(`up deploy --no-build`, "Skip build hooks, useful in CI when a separate build step is used.")
    28  
    29  	cmd.Action(func(_ *kingpin.ParseContext) error {
    30  		return deploy(*stage, !*noBuild)
    31  	})
    32  }
    33  
    34  func deploy(stage string, build bool) error {
    35  retry:
    36  	c, p, err := root.Init()
    37  
    38  	// missing up.json non-interactive
    39  	if isMissingConfig(err) && !term.IsTerminal(os.Stdin.Fd()) {
    40  		return errors.New("Cannot find ./up.json configuration file.")
    41  	}
    42  
    43  	// missing up.json interactive
    44  	if isMissingConfig(err) {
    45  		err := setup.Create()
    46  
    47  		if err == setup.ErrNoCredentials {
    48  			return errors.New("Cannot find credentials, visit https://apex.sh/docs/up/credentials/ for help.")
    49  		}
    50  
    51  		if err != nil {
    52  			return errors.Wrap(err, "setup")
    53  		}
    54  
    55  		util.Log("Deploying the project and creating resources.")
    56  		goto retry
    57  	}
    58  
    59  	// unrelated error
    60  	if err != nil {
    61  		return errors.Wrap(err, "initializing")
    62  	}
    63  
    64  	// validate stage name
    65  	if err := validate.List(stage, c.Stages.RemoteNames()); err != nil {
    66  		return err
    67  	}
    68  
    69  	// stage overrides
    70  	if err := c.Override(stage); err != nil {
    71  		return errors.Wrap(err, "overriding")
    72  	}
    73  
    74  	// git information
    75  	commit, err := getCommit()
    76  	if err != nil {
    77  		return errors.Wrap(err, "fetching git commit")
    78  	}
    79  
    80  	defer util.Pad()()
    81  	start := time.Now()
    82  
    83  	if err := p.Init(stage); err != nil {
    84  		return errors.Wrap(err, "initializing")
    85  	}
    86  
    87  	if err := p.Deploy(up.Deploy{
    88  		Stage:  stage,
    89  		Commit: util.StripLerna(commit.Describe()),
    90  		Author: commit.Author.Name,
    91  		Build:  build,
    92  	}); err != nil {
    93  		return err
    94  	}
    95  
    96  	stats.Track("Deploy", map[string]interface{}{
    97  		"duration":             util.MillisecondsSince(start),
    98  		"type":                 c.Type,
    99  		"regions":              c.Regions,
   100  		"stage":                stage,
   101  		"proxy_timeout":        c.Proxy.Timeout,
   102  		"header_rules_count":   len(c.Headers),
   103  		"redirect_rules_count": len(c.Redirects),
   104  		"inject_rules_count":   len(c.Inject),
   105  		"environment_count":    len(c.Environment),
   106  		"dns_zone_count":       len(c.DNS.Zones),
   107  		"stage_count":          len(c.Stages.List()),
   108  		"stage_domain_count":   len(c.Stages.Domains()),
   109  		"lambda_memory":        c.Lambda.Memory,
   110  		"has_cors":             c.CORS != nil,
   111  		"has_logs":             !c.Logs.Disable,
   112  		"has_profile":          c.Profile != "",
   113  		"has_error_pages":      c.ErrorPages.Enable,
   114  		"app_name_hash":        util.Md5(c.Name),
   115  		"is_git":               commit.Author.Name != "",
   116  	})
   117  
   118  	stats.Flush()
   119  	return nil
   120  }
   121  
   122  // isMissingConfig returns true if the error represents a missing up.json.
   123  func isMissingConfig(err error) bool {
   124  	err = errors.Cause(err)
   125  	e, ok := err.(*os.PathError)
   126  	return ok && e.Path == "up.json"
   127  }
   128  
   129  // getCommit returns the git information when available.
   130  func getCommit() (git.Commit, error) {
   131  	c, err := git.GetCommit(".", "HEAD")
   132  	if err != nil && !isIgnorable(err) {
   133  		return git.Commit{}, err
   134  	}
   135  
   136  	if c == nil {
   137  		return git.Commit{}, nil
   138  	}
   139  
   140  	return *c, nil
   141  }
   142  
   143  // isIgnorable returns true if the GIT error is ignorable.
   144  func isIgnorable(err error) bool {
   145  	switch err {
   146  	case git.ErrLookup, git.ErrNoRepo, git.ErrDirty:
   147  		return true
   148  	default:
   149  		return false
   150  	}
   151  }