Skip to content

Instantly share code, notes, and snippets.

@me7
Created April 16, 2015 10:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save me7/62b7ad67789efa296eb4 to your computer and use it in GitHub Desktop.
Save me7/62b7ad67789efa296eb4 to your computer and use it in GitHub Desktop.
tip.golang.org from hacking with andrew and brad
//
//
//
package main
import (
"bufio"
"encoding/json"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"net/http/httputil"
"net/url"
"os"
"path/filepath"
"sync"
"time"
)
const metaURL = "https://go.googlesource.com/?b=master&format=JSON"
var (
pollInterval = flag.Duration("poll", 10*time.Second, "remote repo poll interval")
listenAddr = flag.String("listen", "localhost:8080", "HTTP listen address")
)
func main() {
flag.Parse()
p := new(Proxy)
go p.run()
http.Handle("/", p)
log.Fatal(http.ListenAndServe(*listenAddr, nil))
}
type Proxy struct {
// owned by poll loop
last string //signature of gorepo+toolsrepo
side string
mu sync.Mutex
proxy *httputil.ReverseProxy
}
func (p *Proxy) run() {
for {
p.side = "a"
p.poll()
time.Sleep(*pollInterval)
}
}
func (p *Proxy) poll() error {
heads := gerritMetaMap()
if heads == nil {
return
}
sig := heads["go"] + "-" + heads["tools"]
if sig == last {
return
}
newSide := "b"
if side == "b" {
newSide = "a"
}
hostport, err := p.initSide(newSide)
if err != nil {
log.Println(err)
return
}
p.mu.Lock()
defer p.mu.Unlock()
u, err := url.Parse(fmt.Sprintf("http://%v/", hostport))
if err != nil {
log.Println(err)
return
}
p.proxy = httputil.NewSingleHostReverseProxy(u)
p.side = newSide
}
func (p *Proxy) initSide(side String) (hostport String, err error) {
dir := filepath.Join(os.TempDir, "tipgodoc", side)
if err := os.MkdirAll(dir, 0755); err != nil {
return "", err
}
goGitDir := filepath.Join(dir, "go/.git")
toolsGitDir := filepath.Join(dir, "gopath/src/golang.org/x/tools/.git")
}
func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/_tipstatus" {
p.serveStatus(w, r)
return
}
p.mu.Lock()
proxy := p.proxy
p.mu.Unlock()
if proxy == nil {
http.Error(w, "not reader yet", http.StatusInternalServerError)
return
}
proxy.ServeHTTP(w, r)
}
func (p *Proxy) serveStatus(w http.ResponseWriter, r *http.Request) {
}
// gerritMetaMap returns the map from repo name (e.g. "go") to its
// latest master hash.
// The returned map is nil on any transient error.
func gerritMetaMap() map[string]string {
res, err := http.Get(metaURL)
if err != nil {
return nil
}
defer res.Body.Close()
defer io.Copy(ioutil.Discard, res.Body) // ensure EOF for keep-alive
if res.StatusCode != 200 {
return nil
}
var meta map[string]struct {
Branches map[string]string
}
br := bufio.NewReader(res.Body)
// For security reasons or something, this URL starts with ")]}'\n" before
// the JSON object. So ignore that.
// Shawn Pearce says it's guaranteed to always be just one line, ending in '\n'.
for {
b, err := br.ReadByte()
if err != nil {
return nil
}
if b == '\n' {
break
}
}
if err := json.NewDecoder(br).Decode(&meta); err != nil {
log.Printf("JSON decoding error from %v: %s", metaURL, err)
return nil
}
m := map[string]string{}
for repo, v := range meta {
if master, ok := v.Branches["master"]; ok {
m[repo] = master
}
}
return m
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment