all repos — memnarch @ 734eab3cfdf4291d064c479653fb07cc194305bf

featherweight orchestrator

main.go (raw)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package main

import (
	"encoding/json"
	"fmt"
	"html/template"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"

	"hacklab.nilfm.cc/quartzgun/renderer"
	"hacklab.nilfm.cc/quartzgun/router"
	. "hacklab.nilfm.cc/quartzgun/util"

	"forge.lightcrystal.systems/lightcrystal/memnarch/action"
	"forge.lightcrystal.systems/lightcrystal/memnarch/webhook"
)

func decode(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		data := make(map[string]interface{})

		err := json.NewDecoder(req.Body).Decode(&data)

		if err == nil {
			AddContextValue(req, "data", data)
		} else {
			AddContextValue(req, "data", err.Error())
		}

		next.ServeHTTP(w, req)
	})
}

func runJob(secret string, next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		// validate signature
		_, err := webhook.Verify([]byte(secret), req)
		if err != nil {
			w.WriteHeader(422)
			return
		}
		// get repo from data
		data := req.Context().Value("data").(map[string]interface{})
		if data != nil {
			w.WriteHeader(500)
			return
		}
		repoUrl := data["repository"].(map[string]interface{})["clone_url"].(string)
		owner := data["owner"].(map[string]interface{})["login"].(string)
		repo := data["repository"].(map[string]interface{})["name"].(string)
		obj := data["head_commit"].(map[string]interface{})["id"].(string)

		// create working dir
		workingDir := filepath.Join("working", owner, repo, obj)
		if os.MkdirAll(workingDir, 0750) != nil {
			w.WriteHeader(500)
			return
		}

		// from this point on we can tell the client they succeeded

		// so we run the rest in a goroutine...
		go func() {
			// cd and checkout repo
			clone := exec.Command("git", "clone", repoUrl)
			clone.Dir = workingDir

			err := clone.Run()
			if err != nil {
				// clone error - log it and quit
				return
			}
			// read memnarch action file
			urlParams := req.Context().Value("params").(map[string]string)
			jobName := urlParams["job"]

			jobFile := filepath.Join(workingDir, repo, jobName+".yml")
			a, err := action.Read(jobFile)

			if err != nil {
				fmt.Println(err.Error())
			}

			// decode and perform action
			// build
			buildCmd := exec.Command(a.Build.Cmd)
			buildCmd.Dir = filepath.Join(workingDir, repo)

			// pre-deploy
			// deploy
			// post-deploy
		}()

		AddContextValue(req, "data", "job submitted")
		next.ServeHTTP(w, req)
	})
}

func seeJobsForRepo(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
	})
}

func seeJobsForObject(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
	})
}

func run(args []string) error {
	secret := args[1]

	rtr := &router.Router{
		Fallback: *template.Must(template.ParseFiles("templates/error.html")),
	}

	rtr.Post("/echo", decode(renderer.JSON("data")))
	rtr.Post(`/do/(?P<job>\S+)`, decode(runJob(secret, renderer.JSON("data"))))
	rtr.Get(`/status/(?P<owner>[^/]+)/(?P<repo>\S+)`, seeJobsForRepo(renderer.JSON("data")))
	rtr.Get(`/status/(?P<owner>[^/]+)/(?P<repo>[^/]+)/(?P<object>\S+)`, seeJobsForObject(renderer.JSON("data")))

	http.ListenAndServe(":9999", rtr)
	return nil
}

func main() {
	err := run(os.Args)
	if err == nil {
		os.Exit(0)
	} else {
		fmt.Println(err.Error())
		os.Exit(1)
	}
}