package main import ( "bytes" "fmt" "log" "os" "os/exec" "strings" "text/template" ) type Services struct { cfg config } func NewServices(cfg config) *Services { return &Services{cfg: cfg} } func (s *Services) AddPrivateKey() error { log.Println("Adding private key") if err := os.MkdirAll(os.ExpandEnv("${HOME}/.ssh"), 0700); err != nil { return err } if err := writeFile(`${HOME}/.ssh/id_rsa`, s.cfg.PrivateKey, 0400); err != nil { return err } return nil } func (s *Services) ConfigureSSH() error { log.Println("Configuring SSH") var bfr bytes.Buffer if err := sshConfigTemplate.Execute(&bfr, struct { Cfg config Home string }{ Cfg: s.cfg, Home: os.Getenv("HOME"), }); err != nil { return err } if err := writeFile(`${HOME}/.ssh/config`, bfr.String(), 0400); err != nil { return err } return nil } func (s *Services) PushRepository() error { sshUrl := fmt.Sprintf(`dokku@%v`, s.cfg.HostName) remoteUrl := fmt.Sprintf(`ssh://%v:22/%v`, sshUrl, s.cfg.AppName) log.Println("Testing Dokku SSH connection") if err := runCmd("ssh", sshUrl, "version"); err != nil { return err } log.Println("SSH connection good. Pushing repository") if err := runCmd("git", "remote", "add", "dokku", remoteUrl); err != nil { return err } sourceBranch := s.cfg.LocalBranch if sourceBranch == "" { // Get the current ref ref, err := runCmdExpectingOutput("git", "rev-parse", "--abbrev-ref", "HEAD") if err != nil { return err } sourceBranch = strings.TrimSpace(ref) } remoteBranch := s.cfg.RemoteBranch if remoteBranch == "" { remoteBranch = sourceBranch } branchArg := sourceBranch if sourceBranch != remoteBranch { branchArg = fmt.Sprintf("%v:%v", sourceBranch, remoteBranch) } if err := runCmd("git", "push", "dokku", branchArg); err != nil { return err } return nil } func runCmd(cmd ...string) error { log.Printf(" .. [exec] %v", strings.Join(cmd, " ")) e := exec.Command(cmd[0], cmd[1:]...) e.Stderr = os.Stderr e.Stdout = os.Stdout return e.Run() } func runCmdExpectingOutput(cmd ...string) (string, error) { log.Printf(" .. [exec] %v", strings.Join(cmd, " ")) e := exec.Command(cmd[0], cmd[1:]...) e.Stderr = os.Stderr res, err := e.Output() if err != nil { return "", err } return string(res), nil } func writeFile(path string, content string, mode os.FileMode) error { fullPath := os.ExpandEnv(path) log.Printf(" .. [file] %v (%v bytes)", fullPath, len(content)) return os.WriteFile(fullPath, []byte(content), mode) } var ( sshConfigTemplate = template.Must(template.New("ssh-config").Parse(` Host * StrictHostKeyChecking no Host {{.Cfg.HostName}} HostName {{.Cfg.HostName}} User dokku IdentityFile {{.Home}}/.ssh/id_rsa `)) )