Backlogãã¼ã ã®nabe_ã§ãããã£ã±ãä»äºã¯Javaã¨Scalaãæè¿ã®è¶£å³ã¯ Goè¨èª ã§ããä»åã Goè¨èª ã§Â nulab/go-git-http-xfer ã¨ãã Git ã©ã¤ãã©ãª ãæ¸ããã®ã§ç´¹ä»ããã¦ããã ãã¾ãã
å½¹å²
ãã®ã©ã¤ãã©ãªã使ãã¨ãGitã®ãªã¢ã¼ããªãã¸ããªã¸HTTPã§ã¢ã¯ã»ã¹ããããã®ãµã¼ãã¼ãä½ããã¨ãã§ãã¾ããHTTPã¢ã¯ã»ã¹èªä½ã¯ãBacklogãGithubçã®Gitããã¹ãã£ã³ã°ãã¦ãããµã¼ãã¹ã§ããã°æ¦ããµãã¼ããã¦ããã®ã§ãæ®æ®µãã¾ãæ°ã«ãããã¨ã¯ãªããã¨æãã¾ãããç¬èªã«Gitãéç¨ãã¦ããå ´åããªã¢ã¼ããªãã¸ããªã®åã«Â cloneãpushãfetch çã§çºçããHTTPéä¿¡ãæãä»çµã¿ãããªã«ãããç¨æããªããã°ãªãã¾ããã
åæ©
ç§èªèº«ã¾ã ã¾ã Goã®ãã¥ã¼ãã¼ãªã®ã§ãããå ã«è§æãåãããªãã¨å§ã¾ããªãããã©ããã ã£ããä»äºã«å½¹ã«ç«ã¤ãã®ãä½ããããã¨ãã£ãåæ©ã§ä½ã£ã¦ã¿ã¾ããã
ã¡ãªã¿ã«ãä»ã®è¨èªã§ãå®è£ ããã¦ãã¦ã
- grackorg/grack Rubyã®rackããã¼ã¹ã«ããå®è£
- dragon3/Plack-App-GitSmartHttp Perlã®plackããã¼ã¹ã«ããå®è£
ã¨ãã£ãã©ã¤ãã©ãªãããã¾ãããã®2ã¤ã®å®è£ ã¯Goã§å®è£ ããä¸ã§ã¨ã¦ãåèã«ãªãã¾ããã
ä»çµã¿
ãªã³ã©ã¤ã³ä½å³ãã¼ã«ã®Cacooã§æããå³ãç¨æãã¾ããã
å®è£ ããã®ã¯Gopherãããç®æã§ãããã£ããã¤ã¡ã¼ã¸ã¨ãã¦ã¯ãã¯ã©ã¤ã¢ã³ãããã®cloneãpushãfetch ã§çºçããHTTPãªã¯ã¨ã¹ããåãåã£ããµã¼ãã¼ããGitã®ãµãã³ãã³ã upload-pack ãããã¯Â receive-pack ãå©ãã¦ããã®çµæãGitã®è»¢éãããã³ã«ã«å¾ã£ã¦ã¬ã¹ãã³ã¹ã¨ãã¦è¿å´ããã¨ãã£ãæµãã«ãªãã¾ãã
è£è¶³ã§ããããã®ãããªGitã§ãããã¯ã¼ã¯è¶ãã«ãã¼ã¿ãåãæ¸¡ãããããã®è¦ç´ã¨ã㦠Smart Protocol ã¨ãããã®ãããã¾ãã(6ã7å¹´åã®Gitã®ãµãã³ãã³ã㯠HTTPã§éä¿¡ããéã« Dumb Protocol ã¨ãããã®ã使ã£ã¦ãã¾ããã)
Smart Protocol:Â 1.6.6 <= git –version
Dumb Protocol:Â git –version <= 1.6.5
ä»åã¯ãSmart Protocol ããã¼ã¹ã«ãµãã³ãã³ãã§çºçããéä¿¡ãä¾ã«ä»çµã¿ãã説æãããã¾ãã
åè:
-
git/Documentation/technical/pack-protocol.txt
-
Gitã®å å´ – ãã©ã³ã¹ãã¡ã¼ãããã³ã« @ git-scm.com
git clone
git clone ã å®è¡ããã¨ãã«ãå é¨ã§çºçããéä¿¡ã®æ§åã§ãã
ã¾ã㯠1 ã®GETãªã¯ã¨ã¹ãã§ãããã¹ã®æ«å°¾ã« /info/refs ãã¯ã¨ãªãã©ã¡ã¼ã¿ã¼ã«service=git-upload-pack ã¨ããæååãä»ä¸ãã¦ãã¾ãããã®ãªã¯ã¨ã¹ããåãåã£ããµã¼ãã¼ã¯ãGitã®ãµãã³ãã³ãã® upload-pack ã«é©åãªãªãã·ã§ã³ãä»ä¸ãã¦å®è¡ãã¾ããupload-pack ã¯ãååç § (ref) ã®ä¸è¦§ï¼ãã¹ã¦ã®ãã©ã³ãã¨ã¿ã°ï¼ãç¾æç¹ã§æã示ãGitãªãã¸ã§ã¯ãã®ID (SHA1ããã·ã¥ã®å¤) ã¨ã¨ãã«åºåããã®ã§ãHTTPã¬ã¹ãã³ã¹ã«ãã®ä¸è¦§ãå«ãã¦ã¯ã©ã¤ã¢ã³ãã«è¿ãã¾ãã
ãªã«ãã³ãããããã¦ããªããã°ããã§çµäºã§ãã
ç¶ãã¦ã 2 ã® POSTãªã¯ã¨ã¹ãã§ãããã¹ã®æ«å°¾ã« /git-upload-pack ãä»ä¸ãã¾ãããªã¯ã¨ã¹ãã®BODYã«ã¯ã1 ã§å¾ãæ å ±ãå ã«ãå¿ è¦ã¨ããGitãªãã¸ã§ã¯ãã®IDã¨æ¢ã«ãã¼ã«ã«ãªãã¸ããªã«æã£ã¦ããGitãªãã¸ã§ã¯ãã®IDãå«ã¾ãã¦ãã¾ãããã®ãªã¯ã¨ã¹ããåãåã£ããµã¼ãã¼ã¯ãGitã®ãµãã³ãã³ãã® upload-pack ã«ãªã¯ã¨ã¹ãã®BODYãå«ãã¦å®è¡ãã¾ããupload-pack 㯠ã¯ã©ã¤ã¢ã³ããè¦æ±ããGitãªãã¸ã§ã¯ãã ããæã¤å§ç¸®ãããããã¯ãã¡ã¤ã«ãåºåããã®ã§ãHTTPã¬ã¹ãã³ã¹ã«ãã®çµæãå«ãã¦ã¯ã©ã¤ã¢ã³ãã«è¿ãã¾ãã
ãã®å§ç¸®ãããããã¯ãã¡ã¤ã«ãå ã«ã¯ã©ã¤ã¢ã³ããã¯ã¼ã¯ããªã¼ãå±éãã¾ãã
ã¯ã©ã¤ã¢ã³ãã git clone ã§ãã¦ã³ãã¼ãããã®ã«å¯¾ãã¦ããµã¼ãã¼ã§ã¯ã¢ãããã¼ãã¨åã®ä»ããµãã³ãã³ããå®è¡ãã¦ãã¾ãããããã¯ããã¾ã§ãµã¼ãã¼ããã¯ã©ã¤ã³ãã«åãã¦ã¢ãããã¼ãããã¨è¨ã£ãæå³åãã«ãªãã¾ãã
git push
git push ã å®è¡ããã¨ãã«ãå é¨ã§çºçããéä¿¡ã®æ§åã§ãã
1 ã®ãªã¯ã¨ã¹ãã¯ãgit clone ã¨ã»ã¼åãã§ãããã¯ã¨ãªãã©ã¡ã¼ã¿ã¼ã«ã¯Â  service=git-receive-pack ã¨ããæååãä»ä¸ãã¦ãã¾ãããµã¼ãã¼ã¯ãGitã®ãµãã³ãã³ãã® receive-pack ãå®è¡ãã¦ãååç §ã®ä¸è¦§ãè¿å´ãã¾ãã
ãã¼ã«ã«ãªãã¸ããªã«å¤æ´(追å /åé¤/æ´æ°)ããªããã°ãããã§çµäºã§ãã
ç¶ãã¦ã2 ã® POSTãªã¯ã¨ã¹ãã§ã¯ããã¹ã®æ«å°¾ã« /git-receive-pack ãä»ä¸ãã¾ããBODYã«ã¯ 1 ã®æ å ±ãå ã«ã夿´ããã£ãååç §(ref)ã示ãå¤ãGitãªãã¸ã§ã¯ãã®IDã¨æ°ããGitãªãã¸ã§ã¯ãã®IDã®ä¸è¦§ãå«ã¾ãã¾ãããµã¼ãã¼ã¯ãGitã®ãµãã³ãã³ãã® receive-pack ã«ãªã¯ã¨ã¹ãã®BODYãå«ããæ°ããGitãªãã¸ã§ã¯ãã®IDã§å¯¾è±¡ã®åç § (ref) ãæ´æ°ããHTTPã¬ã¹ãã³ã¹ã§ãã®çµæ (success or failure) ãè¿ãã¾ãã
ä½¿ãæ¹
ãã®ã©ã¤ãã©ãªã¯ãã²ã¨ã¤ã®æ§é ä½ GitHTTPXfer ãæä¾ãã¾ãããã®æ§é ä½ã¯æ¨æºããã±ã¼ã¸ã®interface http.Handle ã«éµå®ãã¦ã
 func ServeHTTP(ResponseWriter, *Request)
ãå®è£ ãã¦ãã¾ããããã«ãããå©ç¨ããå´ã¯æ¨æºã® http.ListenAndServe çã®æ¨æºã®ãªã¹ãã¼ãå©ç¨ãã¦ãHTTPã§å¾ ã¡åããWebã¢ããªã¨ãã¦èµ·åãããã¨ãã§ãã¾ãã
Basic
ã¤ã³ã¹ã¿ã³ã¹ãçæããéã«ãã³ã³ã¹ãã©ã¯ã¿é¢æ°ã«ã¯æ¸¡ãå¿ é ã®ãã©ã¡ã¼ã¿ã¼ã¨ãã¦ã
- 第ä¸å¼æ°ã® gitRootPath ã«ã¯ãªã¢ã¼ããªãã¸ããªãé ç½®ãããã£ã¬ã¯ããªã®ã«ã¼ããã¹ãè¨å®ãã¾ãã
-  第äºå¼æ°ã® gitBinPath ã«ã¯Gitã³ãã³ãã®ãã¤ããªãã¡ã¤ã«ã®ãã¹ãæå®ãã¾ãã
以ä¸ãåä½ããæä½éã®ã³ã¼ãã§ãã
package main import ( "log" "net/http" "github.com/nulab/go-git-http-xfer/githttpxfer" ) func main() { ghx, err := githttpxfer.New("/your/git/path", "/usr/bin/git") if err != nil { log.Fatal("GitHTTPXfer instance could not be created.", err) return } if err := http.ListenAndServe(":5050", ghx); err != nil { log.Fatal("ListenAndServe: ", err) } }
Optional Parameters
ã¾ããä»»æã§è¨å®ãã颿°åãªãã·ã§ã³ã3ã¤æä¾ãã¦ãã¾ãã
- DisableUploadPack :
clone fetch çã®ãã¼ã¿ããã¦ã³ãã¼ãããæä½ãç¡å¹ã«ãã¾ãã - DisableReceivePack :
push çã®ãã¼ã¿ãã¢ãããã¼ãããæä½ãç¡å¹ã«ãã¾ãã - WithoutDumbProto :
å¤ãè¦ç´ã® Dumb Protocol ã®ãã³ããªã³ã°ãé¤å¤ãã¾ãã
ä¾ãã°ãReadonlyã§å¤ãè¦ç´ãç¡å¹ã«ããå ´åã¯ä»¥ä¸ã®ããã«ãªãã¾ãã
githttpxfer.New( "/your/git/path", "/usr/bin/git", githttpxfer.DisableReceivePack(), githttpxfer.WithoutDumbProto())
Custom Handler
ã³ã³ã¹ãã©ã¯ã¿é¢æ°ã«ãã£ã¦çæããã GitHTTPXfer ã®ã¤ã³ã¹ã¿ã³ã¹ã¯ãå é¨ã«ã«ã¼ã¿ã¼ãæã£ã¦ããhttpã®ãªã¯ã¨ã¹ãã«å¿ãã¦é©åãªå¦çãå®è¡ãã¾ããããã©ã«ãã§ã¯ Smart Protocolã Dumb Protocol ã«å¯¾å¿ãããã³ãã©ãå«ã¾ãã¦ãã¾ããããã®ä»ç¹æã®å¦çã追å ãããã¨ãå¯è½ã§ããä¾ãã°ãGithubã«ããããã¦ã³ãã¼ãæ©è½ããBacklogã«ããããã¬ãã¥ã¼æ©è½çãç¬èªã«å®è£ ãã¦å¯¾å¿ãããã¨ãåºæ¥ã¾ãã
zip 㨠tar ã®ãã¦ã³ãã¼ãæ©è½ã¯ä»»æã§å©ç¨ã§ããããã« addon ããã±ã¼ã¸å ã«å®è£ ãã¦ãã¾ããÂ
package main import ( "net/http" "github.com/nulab/go-git-http-xfer/addon/handler/archive" "github.com/nulab/go-git-http-xfer/githttpxfer" ) func main() { ghx, err := githttpxfer.New("/your/git/path", "/usr/bin/git") if err != nil { log.Fatal("GitHTTPXfer instance could not be created.", err) return } ghx.Router.Add(githttpxfer.NewRoute( archive.Method, archive.Pattern, archive.New(ghx).Archive, )) if err := http.ListenAndServe(":5050", ghx); err != nil { log.Fatal("ListenAndServe: ", err) } }
Event Hooks
GitHTTPXfer ã¯å é¨ã«ã¤ãã³ãã¨ããã¿ã¼ãæã£ã¦ãã¦ãããã¤ãã®ãã¤ã³ãã§ã¤ãã³ããçºç«ããã¦ãã¾ããä¾ãã°ãã¢ãããã¼ãããåã«ãªã«ãåå¦çãå ¥ãã¦ããããæã¯ä»¥ä¸ã®ã¤ãã³ãã§ãã³ããªã³ã°ã§ãã¾ããgit-hook ã«ç¹æã®æ å ±ãæ¸¡ãããã¨ãã¯ãããã§ãã®å¤ããã·ã§ã«å¤æ°ã«å ¥ãã¦ããã¨ãgit-hookã¨ãã¦å®è¡ãããã³ãã³ãã§ãåç §ã§ããããã«ãªãã¾ãã
ghx, err := githttpxfer.New("/data/git", "/usr/bin/git") if err != nil { log.Fatal("GitHTTPXfer instance could not be created.", err) return } ghx.Event.On(githttpxfer.BeforeUploadPack, func(ctx githttpxfer.Context) { // before run service rpc upload. os.Setenv("SOMETHING_KEY", "something value") }) ghx.Event.On(githttpxfer.BeforeReceivePack, func(ctx githttpxfer.Context) { // before run service rpc receive. os.Setenv("SOMETHING_KEY", "something value") })
Middleware
Middleware ãã¿ã¼ã³ã®é¢æ°ã§å ±éå¦çãã¯ãããã¨ãå¯è½ã§ããèªè¨¼å¦çãã¢ã¯ã»ã¹å¶å¾¡çã¯Middlewareã§è¡ããã¨ãæ³å®ãã¦ãã¾ãã
Middleware:Â Â func(http.Handler) http.Handler
ãå®è£
ãã颿°ã弿°ã« http.Handler ãååããæ»ãå¤ã¨ãã¦æ°ãã http.Handler ãè¿ãã
func main() { ghx, err := githttpxfer.New("/data/git", "/usr/bin/git") if err != nil { log.Fatal("GitHTTPXfer instance could not be created.", err) return } handler := Logging(BasicAuth(ghx)) if err := http.ListenAndServe(":5050", handler); err != nil { log.Fatal("ListenAndServe: ", err) } } func Logging(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { t1 := time.Now() next.ServeHTTP(w, r) t2 := time.Now() log.Printf("[%s] %q %v\n", r.Method, r.URL.String(), t2.Sub(t1)) }) } func BasicAuth(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { username, password, ok := r.BasicAuth() if !ok || username != "nulab" || password != "DeaDBeeF" { RenderUnauthorized(w) return } next.ServeHTTP(w, r) }) } func RenderUnauthorized(w http.ResponseWriter) { w.Header().Set("WWW-Authenticate", `Basic realm="Please enter your username and password."`) w.WriteHeader(http.StatusUnauthorized) w.Write([]byte(http.StatusText(http.StatusUnauthorized))) w.Header().Set("Content-Type", "text/plain") }
試ç¨
ã試ãããã«Dockerfileã§ç°å¢ãç¨æãã¦ããã®ã§ã以ä¸ã®ã³ãã³ãã§ããã«èµ·åã§ãã¾ãã
$ git clone https://github.com/nulab/go-git-http-xfer $ cd go-git-http-xfer $ docker build -t go-git-http-xfer . $ docker run -it --rm \ -v $PWD:/go/src/github.com/nulab/go-git-http-xfer -p 5050:5050 \ go-git-http-xfer bash -c "go run ./example/main.go -p 5050" $ git clone http://localhost:5050/example.git
Goè¨èª x Git ãµã¼ãã¼ ã¾ã¨ã
 nulab/go-git-http-xfer ã®ä½¿ãæ¹ã¨ã ãã®ä»çµã¿ã«ã¤ãã¦èª¬æããã¦ããã ãã¾ããã
ããã¾ã§ãGitã®éä¿¡ã®ä»çµã¿ã«ã¤ãã¦æèãããã¨ã¯ã»ã¨ãã©ãªãã£ãã®ã§ããã æãåãããªããå°ãã¥ã¤çè§£ãã¦ãããã¨ã§ããã®éç¨èªä½ã楽ãã¿ãªããé²ããäºãã§ãã¾ããã
ã¾ããä½ã£ã¦ããå½åã¯ãã¤ã³ãã°ã¬ã¼ã·ã§ã³ãã¹ããé¢åããããã ãªã¨æã£ã¦ããã®ã§ãããå ¨ç¶ãããªãã¨ã¯ãªããå ¬å¼ã®dockerã¤ã¡ã¼ã¸ã§ããã«Goã®ç°å¢ãç¨æã§ãããããã¨ã¯Dockerfileã«ã¡ããã£ã¨Gitãã¤ã³ã¹ãã¼ã«ããé¨åã ãæ¸ãã¦ããã°ãããã«ãã¹ãç°å¢ãæ§ç¯ã§ãã¾ããããããã§ããã©ã¤&ã¨ã©ã¼ã§ãã³ãã³é²ãããã¨ãã§ãã¦ããã¤ã³ãããããåã«ãgo fmt ã goimports ããå©ãã¦ããã°ãã³ã¼ãã¹ã¿ã¤ã«ããæ°ã«ãããæ¬è³ªçãªã³ã¼ããæ¸ããã¨ã ãã«éä¸ã§ãã¾ããã
Backlogã§ãGitã®HTTPæ¥ç¶ã¯ãµãã¼ããã¦ããã®ã§ãä»å¾ããã©ã¼ãã³ã¹ãéçºå¹çãéç¨ãããããªã©æ¯è¼ãã¦ã¿ã¦è¯ãçµæãã§ãã°ã¨æå¾ ãã¦ããã¾ãããããªBacklogã§ã¯ä¸ç·ã«éçºããã¦ãããã¡ã³ãã¼ãåéãã¦ãã¾ãã