blob: b560c6c075dcfd42d4203478518edfd59ce234d9 [file] [log] [blame]
package main
import (
"encoding/json"
"flag"
"fmt"
"log"
"net"
"os"
"strings"
"golang.org/x/crypto/ssh"
"github.com/go-git/go-billy/v5/osfs"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
gitssh "github.com/go-git/go-git/v5/plumbing/transport/ssh"
"github.com/go-git/go-git/v5/storage/memory"
)
var port = flag.Int("port", 3000, "Port to listen on")
var appId = flag.String("app-id", "", "Application ID")
var repoAddr = flag.String("repo-addr", "", "Git repository address")
var branch = flag.String("branch", "", "Name of the branch to process")
var rootDir = flag.String("root-dir", "/", "Path to the app code")
var sshKey = flag.String("ssh-key", "", "Private SSH key to access Git repository")
var appDir = flag.String("app-dir", "", "Path to store application repository locally")
var runCfg = flag.String("run-cfg", "", "Run configuration")
var managerAddr = flag.String("manager-addr", "", "Address of the manager")
type Command struct {
Bin string `json:"bin"`
Args []string `json:"args"`
Env []string `json:"env"`
}
func CloneRepositoryBranch(addr, branch, rootDir string, signer ssh.Signer, path string) error {
ref := fmt.Sprintf("refs/heads/%s", branch)
opts := &git.CloneOptions{
URL: addr,
RemoteName: "origin",
ReferenceName: plumbing.ReferenceName(ref),
SingleBranch: true,
Depth: 1,
InsecureSkipTLS: true,
Progress: os.Stdout,
}
if signer != nil {
opts.Auth = &gitssh.PublicKeys{
User: "git",
Signer: signer,
HostKeyCallbackHelper: gitssh.HostKeyCallbackHelper{
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
// TODO(giolekva): verify server public key
fmt.Printf("--- %+v\n", ssh.MarshalAuthorizedKey(key))
return nil
},
},
}
}
c, err := git.Clone(memory.NewStorage(), osfs.New(path, osfs.WithBoundOS()), opts)
if err != nil {
return err
}
wt, err := c.Worktree()
if err != nil {
return err
}
if wt == nil {
panic(wt)
}
// TODO(gio): This should probably be removed.
// sb, err := wt.Submodules()
// if err != nil {
// return err
// }
// if err := sb.Init(); err != nil {
// return err
// }
// if err := sb.Update(&git.SubmoduleUpdateOptions{
// Depth: 1,
// }); err != nil {
// return err
// }
return err
}
func main() {
flag.Parse()
self, ok := os.LookupEnv("SELF_IP")
if !ok {
panic("no SELF_IP")
}
var signer ssh.Signer
// TODO(gio): revisit this logic
if *sshKey != "" && !(strings.HasPrefix(*repoAddr, "http://") || strings.HasPrefix(*repoAddr, "https://")) {
key, err := os.ReadFile(*sshKey)
if err != nil {
panic(err)
}
signer, err = ssh.ParsePrivateKey(key)
if err != nil {
panic(err)
}
}
if err := CloneRepositoryBranch(*repoAddr, *branch, *rootDir, signer, *appDir); err != nil {
panic(err)
}
r, err := os.Open(*runCfg)
if err != nil {
panic(err)
}
defer r.Close()
var cmds []Command
if err := json.NewDecoder(r).Decode(&cmds); err != nil {
panic(err)
}
s := NewServer(*port, *appId, *repoAddr, *branch, *rootDir, signer, *appDir, cmds, self, *managerAddr)
if err := s.Start(); err != nil {
log.Fatal(err)
}
}