package main /* Listen for calls from GitHub and update local repositories. */ import ( "encoding/json" "fmt" "io/ioutil" "log" "net/http" "os" "os/exec" "os/user" "path/filepath" "strings" "time" ) var gopath string func init() { // Set gopath to GOPATH or $HOME (the default) gp := os.Getenv("GOPATH") if gp == "" { u, err := user.Current() if err != nil { log.Printf("Failed to check home dir: %s\n", err) } else { gp = u.HomeDir } } gopath = filepath.Join(gp, "src") } // event represents a JSONfile from GitHub. // It is a small subset of the actual data GitHub provides. type event struct { Repository repo `json:"repository"` Action string CommitID string `json:"after"` Branch string `json:"ref"` path string } type repo struct { URL string `json:"html_url"` Name string `json:"name"` FullName string `json:"full_name"` } func (e event) String() string { return fmt.Sprintf("%s %s", e.Repository.URL, e.Action) } func main() { http.HandleFunc("/", catch) http.ListenAndServe(":7000", nil) } func catch(w http.ResponseWriter, r *http.Request) { defer w.Write([]byte("OK")) body, err := ioutil.ReadAll(r.Body) if err != nil { log.Printf("Failed to read body: %s\n") return } r.Body.Close() filename := fmt.Sprintf("TEMP_%s_%s.json", r.RemoteAddr, time.Now().Format("2006-01-02_150405")) err = ioutil.WriteFile(filename, body, 0644) if err != nil { log.Printf("Failed to write body: %s\n") return } e, err := newEvent(body) if err != nil { log.Printf("Failed to parse event from JSON: %s\n", err) return } go logEvent(e, filename) if e.CommitID != "" { go updateLocal(e) } } func newEvent(body []byte) (*event, error) { e := &event{} err := json.Unmarshal(body, e) if err != nil { return e, err } head := "refs/heads/" if e.CommitID != "" && strings.HasPrefix(e.Branch, head) { e.Branch = e.Branch[len(head):] } prefix := "https://" if strings.HasPrefix(e.Repository.URL, prefix) { e.path = filepath.Join(gopath, e.Repository.URL[len(prefix):]) } return e, err } func logEvent(e *event, filename string) { log.Printf("%s: %s\n", filename, e) fmt.Printf("Event %#v\n", e) if e.CommitID != "" { fmt.Printf("Commit %s\n", e.CommitID) } } func updateLocal(e *event) { /* Go to dir check if dir clean check out branch fetch & pull */ cmd := exec.Command("git", "diff") cmd.Dir = e.path out, err := cmd.CombinedOutput() if err != nil { log.Printf("Error running combined output: %s\n", err) return } if len(out) > 0 { log.Printf("%s directory not clean.\n", e.path) return } cmd = exec.Command("git", "checkout", e.Branch) cmd.Dir = e.path if err = cmd.Run(); err != nil { log.Printf("Failed to check out branch: %s\n", err) return } log.Printf("Checked out %s %s\n", e.Repository, e.Branch) cmd = exec.Command("git", "fetch") cmd.Dir = e.path if err = cmd.Run(); err != nil { log.Printf("Failed to fetch: %s\n", err) return } log.Printf("Fetched %s %s\n", e.Repository, e.Branch) cmd = exec.Command("git", "pull", "origin", e.Branch) cmd.Dir = e.path if err = cmd.Run(); err != nil { log.Printf("Failed to pull branch %s: %s\n", e.Branch, err) return } log.Printf("Pulled %s %s\n", e.Repository, e.Branch) }