diff --git a/action.yaml b/action.yaml new file mode 100644 index 0000000..1d4c370 --- /dev/null +++ b/action.yaml @@ -0,0 +1,14 @@ +name: 'Push to Dokku' +description: 'Pushes a repository to a remote Dokku host' +runs: + using: 'go' + main: 'main.go' +inputs: + host: + description: 'Remote hostname' + required: true + app: + description: 'App name' + required: true + private-key: + description: 'Private SSH key to connect to remote host' \ No newline at end of file diff --git a/config.go b/config.go new file mode 100644 index 0000000..9c3300c --- /dev/null +++ b/config.go @@ -0,0 +1,17 @@ +package main + +import "os" + +type config struct { + HostName string + AppName string + PrivateKey string +} + +func readConfig() config { + return config{ + HostName: os.Getenv("host"), + AppName: os.Getenv("app"), + PrivateKey: os.Getenv("private-key"), + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..927bdb0 --- /dev/null +++ b/go.mod @@ -0,0 +1,10 @@ +module lmika.dev/actions/push-to-dokku + +go 1.22.4 + +require ( + github.com/bitfield/script v0.22.1 // indirect + github.com/itchyny/gojq v0.12.13 // indirect + github.com/itchyny/timefmt-go v0.1.5 // indirect + mvdan.cc/sh/v3 v3.7.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..8130710 --- /dev/null +++ b/go.sum @@ -0,0 +1,8 @@ +github.com/bitfield/script v0.22.1 h1:DphxoC5ssYciwd0ZS+N0Xae46geAD/0mVWh6a2NUxM4= +github.com/bitfield/script v0.22.1/go.mod h1:fv+6x4OzVsRs6qAlc7wiGq8fq1b5orhtQdtW0dwjUHI= +github.com/itchyny/gojq v0.12.13 h1:IxyYlHYIlspQHHTE0f3cJF0NKDMfajxViuhBLnHd/QU= +github.com/itchyny/gojq v0.12.13/go.mod h1:JzwzAqenfhrPUuwbmEz3nu3JQmFLlQTQMUcOdnu/Sf4= +github.com/itchyny/timefmt-go v0.1.5 h1:G0INE2la8S6ru/ZI5JecgyzbbJNs5lG1RcBqa7Jm6GE= +github.com/itchyny/timefmt-go v0.1.5/go.mod h1:nEP7L+2YmAbT2kZ2HfSs1d8Xtw9LY8D2stDBckWakZ8= +mvdan.cc/sh/v3 v3.7.0 h1:lSTjdP/1xsddtaKfGg7Myu7DnlHItd3/M2tomOcNNBg= +mvdan.cc/sh/v3 v3.7.0/go.mod h1:K2gwkaesF/D7av7Kxl0HbF5kGOd2ArupNTX3X44+8l8= diff --git a/main.go b/main.go new file mode 100644 index 0000000..7190c14 --- /dev/null +++ b/main.go @@ -0,0 +1,22 @@ +package main + +import "log" + +func main() { + cfg := readConfig() + log.Println("Reading config") + + svc := NewServices(cfg) + + if err := svc.AddPrivateKey(); err != nil { + log.Fatal(err) + } + + if err := svc.ConfigureSSH(); err != nil { + log.Fatal(err) + } + + if err := svc.PushRepository(); err != nil { + log.Fatal(err) + } +} diff --git a/services.go b/services.go new file mode 100644 index 0000000..a29eee4 --- /dev/null +++ b/services.go @@ -0,0 +1,98 @@ +package main + +import ( + "bytes" + "fmt" + "github.com/bitfield/script" + "log" + "os" + "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 := script.Exec(`mkdir -p $HOME/.ssh`).Error(); err != nil { + return err + } + + if err := script.Exec(`chmod 700 $HOME/.ssh`).Error(); err != nil { + return err + } + + _, err := script.Echo(s.cfg.PrivateKey).WriteFile(os.ExpandEnv(`${HOME}/.ssh/id_rsa`)) + if err != nil { + return err + } + + if err := script.Exec(`chmod -R 400 $HOME/.ssh/.`).Error(); 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 := script.Echo(bfr.String()).WriteFile(os.ExpandEnv(`${HOME}/.ssh/config`)); err != nil { + return err + } + if err := script.Exec(`chmod 400 $HOME/.ssh/config`).Error(); 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 := script.Exec(fmt.Sprintf(`ssh %v version`, sshUrl)).Error(); err != nil { + return err + } + + log.Println("SSH connection good. Pushing repository") + if err := script.Exec(fmt.Sprintf(`git remote add dokku '%v'`, remoteUrl)).Error(); err != nil { + return err + } + + if err := script.Exec(`git push dokku main`).Error(); err != nil { + return err + } + + return nil +} + +var ( + sshConfigTemplate = template.Must(template.New("ssh-config").Parse(` +Host * + StrictHostKeyChecking no + +Host {{.Cfg.Host}} + HostName {{.Cfg.Host}} + User dokku + IdentityFile {{.Home}}/.ssh/id_rsa +`)) +)