Default runtime now checks out code on demand (#1563)
* Default runtime now checks out code on demand * Go mod tidy
This commit is contained in:
		
							
								
								
									
										3
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								go.mod
									
									
									
									
									
								
							| @@ -22,6 +22,7 @@ require ( | ||||
| 	github.com/fsouza/go-dockerclient v1.6.0 | ||||
| 	github.com/ghodss/yaml v1.0.0 | ||||
| 	github.com/go-acme/lego/v3 v3.3.0 | ||||
| 	github.com/go-git/go-git/v5 v5.0.0 | ||||
| 	github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible // indirect | ||||
| 	github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee | ||||
| 	github.com/gobwas/pool v0.2.0 // indirect | ||||
| @@ -61,7 +62,7 @@ require ( | ||||
| 	go.etcd.io/bbolt v1.3.4 | ||||
| 	go.uber.org/zap v1.13.0 | ||||
| 	golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 | ||||
| 	golang.org/x/net v0.0.0-20200222125558-5a598a2470a0 | ||||
| 	golang.org/x/net v0.0.0-20200301022130-244492dfa37a | ||||
| 	golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect | ||||
| 	google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1 | ||||
| 	google.golang.org/grpc v1.26.0 | ||||
|   | ||||
							
								
								
									
										27
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								go.sum
									
									
									
									
									
								
							| @@ -101,6 +101,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma | ||||
| github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= | ||||
| github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= | ||||
| github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= | ||||
| github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= | ||||
| github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
| github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||||
| github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
| @@ -146,6 +147,14 @@ github.com/go-acme/lego/v3 v3.3.0 h1:6BePZsOiYA4/w+M7QDytxQtMfCipMPGnWAHs9pWks98 | ||||
| github.com/go-acme/lego/v3 v3.3.0/go.mod h1:iGSY2vQrvQs3WezicSB/oVbO2eCrD88dpWPwb1qLqu0= | ||||
| github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s= | ||||
| github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= | ||||
| github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= | ||||
| github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= | ||||
| github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM= | ||||
| github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= | ||||
| github.com/go-git/go-git-fixtures/v4 v4.0.1 h1:q+IFMfLx200Q3scvt2hN79JsEzy4AmBTp/pqnefH+Bc= | ||||
| github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw= | ||||
| github.com/go-git/go-git/v5 v5.0.0 h1:k5RWPm4iJwYtfWoxIJy4wJX9ON7ihPeZZYC1fLYDnpg= | ||||
| github.com/go-git/go-git/v5 v5.0.0/go.mod h1:oYD8y9kWsGINPFJoLdaScGCN6dlKg23blmClfZwtUVA= | ||||
| github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= | ||||
| github.com/go-ini/ini v1.44.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= | ||||
| github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= | ||||
| @@ -263,6 +272,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= | ||||
| github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= | ||||
| github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= | ||||
| github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= | ||||
| github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= | ||||
| github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= | ||||
| github.com/labbsr0x/bindman-dns-webhook v1.0.2/go.mod h1:p6b+VCXIR8NYKpDr8/dg1HKfQoRHCdcsROXKvmoehKA= | ||||
| github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w= | ||||
| github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU= | ||||
| @@ -317,6 +328,8 @@ github.com/nats-io/nkeys v0.1.4/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1t | ||||
| github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= | ||||
| github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= | ||||
| github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= | ||||
| github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= | ||||
| github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= | ||||
| github.com/nlopes/slack v0.6.1-0.20191106133607-d06c2a2b3249 h1:Pr5gZa2VcmktVwq0lyC39MsN5tz356vC/pQHKvq+QBo= | ||||
| github.com/nlopes/slack v0.6.1-0.20191106133607-d06c2a2b3249/go.mod h1:JzQ9m3PMAqcpeCam7UaHSuBuupz7CmpjehYMayT6YOk= | ||||
| github.com/nrdcg/auroradns v1.0.0/go.mod h1:6JPXKzIRzZzMqtTDgueIhTi6rFf1QvYE/HzqidhOhjw= | ||||
| @@ -385,6 +398,8 @@ github.com/sacloud/libsacloud v1.26.1/go.mod h1:79ZwATmHLIFZIMd7sxA3LwzVy/B77uj3 | ||||
| github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= | ||||
| github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= | ||||
| github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= | ||||
| github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= | ||||
| github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= | ||||
| github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= | ||||
| github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= | ||||
| github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= | ||||
| @@ -456,6 +471,7 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U | ||||
| golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||
| golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||
| golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||
| golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||||
| golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 h1:3zb4D3T4G8jdExgVU/95+vQXfpEPiMdCaZgmGVxjNHM= | ||||
| golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||||
| golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | ||||
| @@ -500,8 +516,8 @@ golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLL | ||||
| golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20190930134127-c5a3c61f89f3/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20191027093000-83d349e8ac1a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20200222125558-5a598a2470a0 h1:MsuvTghUPjX762sGLnGsxC3HM0B5r83wEtYcYR8/vRs= | ||||
| golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0= | ||||
| golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= | ||||
| golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||
| golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||
| @@ -539,6 +555,8 @@ golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe h1:6fAMxZRR6sl1Uq8U61gxU+kPT | ||||
| golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 h1:LfCXLvNmTYH9kEmVgqbnsWfruoXZIrh4YBgqVHtDvw0= | ||||
| golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So= | ||||
| golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||
| golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||
| golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= | ||||
| @@ -612,6 +630,9 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks | ||||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= | ||||
| gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= | ||||
| gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= | ||||
| gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= | ||||
| gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= | ||||
| @@ -639,6 +660,8 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl | ||||
| gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||
| gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= | ||||
| gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||
| gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= | ||||
| gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||
| gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= | ||||
| gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= | ||||
| honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||||
|   | ||||
| @@ -13,6 +13,7 @@ import ( | ||||
|  | ||||
| 	"github.com/hpcloud/tail" | ||||
| 	"github.com/micro/go-micro/v2/logger" | ||||
| 	"github.com/micro/go-micro/v2/runtime/local/git" | ||||
| ) | ||||
|  | ||||
| type runtime struct { | ||||
| @@ -52,6 +53,21 @@ func NewRuntime(opts ...Option) Runtime { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // @todo move this to runtime default | ||||
| func (r *runtime) checkoutSourceIfNeeded(s *Service) error { | ||||
| 	source, err := git.ParseSourceLocal("", s.Source) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	source.Ref = s.Version | ||||
| 	err = git.CheckoutSource(os.TempDir(), source) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	s.Source = source.FullPath | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Init initializes runtime options | ||||
| func (r *runtime) Init(opts ...Option) error { | ||||
| 	r.Lock() | ||||
| @@ -193,6 +209,7 @@ func serviceKey(s *Service) string { | ||||
|  | ||||
| // Create creates a new service which is then started by runtime | ||||
| func (r *runtime) Create(s *Service, opts ...CreateOption) error { | ||||
| 	r.checkoutSourceIfNeeded(s) | ||||
| 	r.Lock() | ||||
| 	defer r.Unlock() | ||||
|  | ||||
| @@ -336,6 +353,7 @@ func (r *runtime) Read(opts ...ReadOption) ([]*Service, error) { | ||||
|  | ||||
| // Update attemps to update the service | ||||
| func (r *runtime) Update(s *Service, opts ...UpdateOption) error { | ||||
| 	r.checkoutSourceIfNeeded(s) | ||||
| 	r.Lock() | ||||
| 	service, ok := r.services[serviceKey(s)] | ||||
| 	r.Unlock() | ||||
|   | ||||
							
								
								
									
										333
									
								
								runtime/local/git/git.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										333
									
								
								runtime/local/git/git.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,333 @@ | ||||
| package git | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"os/exec" | ||||
| 	"path/filepath" | ||||
| 	"regexp" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/go-git/go-git/v5" | ||||
| 	"github.com/go-git/go-git/v5/config" | ||||
| 	"github.com/go-git/go-git/v5/plumbing" | ||||
| ) | ||||
|  | ||||
| type Gitter interface { | ||||
| 	Clone(repo string) error | ||||
| 	FetchAll(repo string) error | ||||
| 	Checkout(repo, branchOrCommit string) error | ||||
| 	RepoDir(repo string) string | ||||
| } | ||||
|  | ||||
| type libGitter struct { | ||||
| 	folder string | ||||
| } | ||||
|  | ||||
| func (g libGitter) Clone(repo string) error { | ||||
| 	fold := filepath.Join(g.folder, dirifyRepo(repo)) | ||||
| 	exists, err := pathExists(fold) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if exists { | ||||
| 		return nil | ||||
| 	} | ||||
| 	_, err = git.PlainClone(fold, false, &git.CloneOptions{ | ||||
| 		URL:      repo, | ||||
| 		Progress: os.Stdout, | ||||
| 	}) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| func (g libGitter) FetchAll(repo string) error { | ||||
| 	repos, err := git.PlainOpen(filepath.Join(g.folder, dirifyRepo(repo))) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	remotes, err := repos.Remotes() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	err = remotes[0].Fetch(&git.FetchOptions{ | ||||
| 		RefSpecs: []config.RefSpec{"refs/*:refs/*", "HEAD:refs/heads/HEAD"}, | ||||
| 		Progress: os.Stdout, | ||||
| 		Depth:    1, | ||||
| 	}) | ||||
| 	if err != nil && err != git.NoErrAlreadyUpToDate { | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (g libGitter) Checkout(repo, branchOrCommit string) error { | ||||
| 	if branchOrCommit == "latest" { | ||||
| 		branchOrCommit = "master" | ||||
| 	} | ||||
| 	repos, err := git.PlainOpen(filepath.Join(g.folder, dirifyRepo(repo))) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	worktree, err := repos.Worktree() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	isCommit := func(s string) bool { | ||||
| 		return strings.ContainsAny(s, "0123456789") && len(s) == 40 | ||||
| 	} | ||||
| 	if isCommit(branchOrCommit) { | ||||
| 		err = worktree.Checkout(&git.CheckoutOptions{ | ||||
| 			Hash:  plumbing.NewHash(branchOrCommit), | ||||
| 			Force: true, | ||||
| 		}) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} else { | ||||
| 		err = worktree.Checkout(&git.CheckoutOptions{ | ||||
| 			Branch: plumbing.NewBranchReferenceName(branchOrCommit), | ||||
| 			Force:  true, | ||||
| 		}) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (g libGitter) RepoDir(repo string) string { | ||||
| 	return filepath.Join(g.folder, dirifyRepo(repo)) | ||||
| } | ||||
|  | ||||
| type binaryGitter struct { | ||||
| 	folder string | ||||
| } | ||||
|  | ||||
| func (g binaryGitter) Clone(repo string) error { | ||||
| 	fold := filepath.Join(g.folder, dirifyRepo(repo)) | ||||
| 	exists, err := pathExists(fold) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if exists { | ||||
| 		return nil | ||||
| 	} | ||||
| 	cmd := exec.Command("git", "clone", repo, ".") | ||||
|  | ||||
| 	err = os.MkdirAll(fold, 0777) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	cmd.Dir = fold | ||||
| 	_, err = cmd.Output() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| func (g binaryGitter) FetchAll(repo string) error { | ||||
| 	cmd := exec.Command("git", "fetch", "--all") | ||||
| 	cmd.Dir = filepath.Join(g.folder, dirifyRepo(repo)) | ||||
| 	_, err := cmd.Output() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| func (g binaryGitter) Checkout(repo, branchOrCommit string) error { | ||||
| 	if branchOrCommit == "latest" { | ||||
| 		branchOrCommit = "master" | ||||
| 	} | ||||
| 	cmd := exec.Command("git", "checkout", "-f", branchOrCommit) | ||||
| 	cmd.Dir = filepath.Join(g.folder, dirifyRepo(repo)) | ||||
| 	_, err := cmd.Output() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (g binaryGitter) RepoDir(repo string) string { | ||||
| 	return filepath.Join(g.folder, dirifyRepo(repo)) | ||||
| } | ||||
|  | ||||
| func NewGitter(folder string) Gitter { | ||||
| 	if commandExists("git") { | ||||
| 		return binaryGitter{folder} | ||||
| 	} | ||||
| 	return libGitter{folder} | ||||
| } | ||||
|  | ||||
| func commandExists(cmd string) bool { | ||||
| 	_, err := exec.LookPath(cmd) | ||||
| 	return err == nil | ||||
| } | ||||
|  | ||||
| func dirifyRepo(s string) string { | ||||
| 	s = strings.ReplaceAll(s, "https://", "") | ||||
| 	s = strings.ReplaceAll(s, "/", "-") | ||||
| 	return s | ||||
| } | ||||
|  | ||||
| // exists returns whether the given file or directory exists | ||||
| func pathExists(path string) (bool, error) { | ||||
| 	_, err := os.Stat(path) | ||||
| 	if err == nil { | ||||
| 		return true, nil | ||||
| 	} | ||||
| 	if os.IsNotExist(err) { | ||||
| 		return false, nil | ||||
| 	} | ||||
| 	return true, err | ||||
| } | ||||
|  | ||||
| // GetRepoRoot determines the repo root from a full path. | ||||
| // Returns empty string and no error if not found | ||||
| func GetRepoRoot(fullPath string) (string, error) { | ||||
| 	// traverse parent directories | ||||
| 	prev := fullPath | ||||
| 	for { | ||||
| 		current := prev | ||||
| 		exists, err := pathExists(filepath.Join(current, ".git")) | ||||
| 		if err != nil { | ||||
| 			return "", err | ||||
| 		} | ||||
| 		if exists { | ||||
| 			return current, nil | ||||
| 		} | ||||
| 		prev = filepath.Dir(current) | ||||
| 		// reached top level, see: | ||||
| 		// https://play.golang.org/p/rDgVdk3suzb | ||||
| 		if current == prev { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	return "", nil | ||||
| } | ||||
|  | ||||
| const defaultRepo = "github.com/micro/services" | ||||
|  | ||||
| // Source is not just git related @todo move | ||||
| type Source struct { | ||||
| 	// is it a local folder intended for a local runtime? | ||||
| 	Local bool | ||||
| 	// absolute path to service folder in local mode | ||||
| 	FullPath string | ||||
| 	// path of folder to repo root | ||||
| 	// be it local or github repo | ||||
| 	Folder string | ||||
| 	// github ref | ||||
| 	Ref string | ||||
| 	// for cloning purposes | ||||
| 	// blank for local | ||||
| 	Repo string | ||||
| 	// dir to repo root | ||||
| 	// blank for non local | ||||
| 	LocalRepoRoot string | ||||
| } | ||||
|  | ||||
| // Name to be passed to RPC call runtime.Create Update Delete | ||||
| // eg: `helloworld/api`, `crufter/myrepo/helloworld/api`, `localfolder` | ||||
| func (s *Source) RuntimeName() string { | ||||
| 	if s.Repo == "github.com/micro/services" || s.Repo == "" { | ||||
| 		return s.Folder | ||||
| 	} | ||||
| 	return fmt.Sprintf("%v/%v", strings.ReplaceAll(s.Repo, "github.com/", ""), s.Folder) | ||||
| } | ||||
|  | ||||
| // Source to be passed to RPC call runtime.Create Update Delete | ||||
| // eg: `helloworld`, `github.com/crufter/myrepo/helloworld`, `/path/to/localrepo/localfolder` | ||||
| func (s *Source) RuntimeSource() string { | ||||
| 	if s.Local { | ||||
| 		return s.FullPath | ||||
| 	} | ||||
| 	if s.Repo == "github.com/micro/services" || s.Repo == "" { | ||||
| 		return s.Folder | ||||
| 	} | ||||
| 	return fmt.Sprintf("%v/%v", s.Repo, s.Folder) | ||||
| } | ||||
|  | ||||
| // ParseSource parses a `micro run/update/kill` source. | ||||
| func ParseSource(source string) (*Source, error) { | ||||
| 	// If github is not present, we got a shorthand for `micro/services` | ||||
| 	if !strings.Contains(source, "github.com") { | ||||
| 		source = "github.com/micro/services/" + source | ||||
| 	} | ||||
| 	if !strings.Contains(source, "@") { | ||||
| 		source += "@latest" | ||||
| 	} | ||||
| 	ret := &Source{} | ||||
| 	refs := strings.Split(source, "@") | ||||
| 	ret.Ref = refs[1] | ||||
| 	parts := strings.Split(refs[0], "/") | ||||
| 	ret.Repo = strings.Join(parts[0:3], "/") | ||||
| 	if len(parts) > 1 { | ||||
| 		ret.Folder = strings.Join(parts[3:], "/") | ||||
| 	} | ||||
|  | ||||
| 	return ret, nil | ||||
| } | ||||
|  | ||||
| // ParseSourceLocal detects and handles local pathes too | ||||
| // workdir should be used only from the CLI @todo better interface for this function | ||||
| func ParseSourceLocal(workDir, source string) (*Source, error) { | ||||
| 	var localFullPath string | ||||
| 	if len(workDir) > 0 { | ||||
| 		localFullPath = filepath.Join(workDir, source) | ||||
| 	} else { | ||||
| 		localFullPath = source | ||||
| 	} | ||||
| 	if exists, err := pathExists(localFullPath); err == nil && exists { | ||||
| 		localRepoRoot, err := GetRepoRoot(localFullPath) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		folder := strings.ReplaceAll(localFullPath, localRepoRoot+string(filepath.Separator), "") | ||||
| 		return &Source{ | ||||
| 			Local:         true, | ||||
| 			Folder:        folder, | ||||
| 			FullPath:      localFullPath, | ||||
| 			LocalRepoRoot: localRepoRoot, | ||||
| 			Ref:           "latest", // @todo consider extracting branch from git here | ||||
| 		}, nil | ||||
| 	} | ||||
| 	return ParseSource(source) | ||||
| } | ||||
|  | ||||
| // CheckoutSource for the local runtime server | ||||
| // folder is the folder to check out the source code to | ||||
| // Modifies source path to set it to checked out repo absolute path locally. | ||||
| func CheckoutSource(folder string, source *Source) error { | ||||
| 	// if it's a local folder, do nothing | ||||
| 	if exists, err := pathExists(source.FullPath); err == nil && exists { | ||||
| 		return nil | ||||
| 	} | ||||
| 	gitter := NewGitter(folder) | ||||
| 	repo := source.Repo | ||||
| 	if !strings.Contains(repo, "https://") { | ||||
| 		repo = "https://" + repo | ||||
| 	} | ||||
| 	// Always clone, it's idempotent and only clones if needed | ||||
| 	err := gitter.Clone(repo) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	source.FullPath = filepath.Join(gitter.RepoDir(source.Repo), source.Folder) | ||||
| 	return gitter.Checkout(repo, source.Ref) | ||||
| } | ||||
|  | ||||
| // code below is not used yet | ||||
|  | ||||
| var nameExtractRegexp = regexp.MustCompile(`((micro|web)\.Name\(")(.*)("\))`) | ||||
|  | ||||
| func extractServiceName(fileContent []byte) string { | ||||
| 	hits := nameExtractRegexp.FindAll(fileContent, 1) | ||||
| 	if len(hits) == 0 { | ||||
| 		return "" | ||||
| 	} | ||||
| 	hit := string(hits[0]) | ||||
| 	return strings.Split(hit, "\"")[1] | ||||
| } | ||||
							
								
								
									
										95
									
								
								runtime/local/git/git_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								runtime/local/git/git_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,95 @@ | ||||
| package git | ||||
|  | ||||
| import ( | ||||
| 	"testing" | ||||
| ) | ||||
|  | ||||
| type parseCase struct { | ||||
| 	url      string | ||||
| 	expected *Source | ||||
| } | ||||
|  | ||||
| func TestParseSource(t *testing.T) { | ||||
| 	cases := []parseCase{ | ||||
| 		{ | ||||
| 			url: "helloworld", | ||||
| 			expected: &Source{ | ||||
| 				Repo:   "github.com/micro/services", | ||||
| 				Folder: "helloworld", | ||||
| 				Ref:    "latest", | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			url: "github.com/micro/services/helloworld", | ||||
| 			expected: &Source{ | ||||
| 				Repo:   "github.com/micro/services", | ||||
| 				Folder: "helloworld", | ||||
| 				Ref:    "latest", | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			url: "github.com/micro/services/helloworld@v1.12.1", | ||||
| 			expected: &Source{ | ||||
| 				Repo:   "github.com/micro/services", | ||||
| 				Folder: "helloworld", | ||||
| 				Ref:    "v1.12.1", | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			url: "github.com/micro/services/helloworld@branchname", | ||||
| 			expected: &Source{ | ||||
| 				Repo:   "github.com/micro/services", | ||||
| 				Folder: "helloworld", | ||||
| 				Ref:    "branchname", | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			url: "github.com/crufter/reponame/helloworld@branchname", | ||||
| 			expected: &Source{ | ||||
| 				Repo:   "github.com/crufter/reponame", | ||||
| 				Folder: "helloworld", | ||||
| 				Ref:    "branchname", | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	for i, c := range cases { | ||||
| 		result, err := ParseSource(c.url) | ||||
| 		if err != nil { | ||||
| 			t.Fatalf("Failed case %v: %v", i, err) | ||||
| 		} | ||||
| 		if result.Folder != c.expected.Folder { | ||||
| 			t.Fatalf("Folder does not match for '%v', expected '%v', got '%v'", i, c.expected.Folder, result.Folder) | ||||
| 		} | ||||
| 		if result.Repo != c.expected.Repo { | ||||
| 			t.Fatalf("Repo address does not match for '%v', expected '%v', got '%v'", i, c.expected.Repo, result.Repo) | ||||
| 		} | ||||
| 		if result.Ref != c.expected.Ref { | ||||
| 			t.Fatalf("Ref does not match for '%v', expected '%v', got '%v'", i, c.expected.Ref, result.Ref) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| type nameCase struct { | ||||
| 	fileContent string | ||||
| 	expected    string | ||||
| } | ||||
|  | ||||
| func TestServiceNameExtract(t *testing.T) { | ||||
| 	cases := []nameCase{ | ||||
| 		{ | ||||
| 			fileContent: `func main() { | ||||
| 			// New Service | ||||
| 			service := micro.NewService( | ||||
| 				micro.Name("go.micro.service.helloworld"), | ||||
| 				micro.Version("latest"), | ||||
| 			)`, | ||||
| 			expected: "go.micro.service.helloworld", | ||||
| 		}, | ||||
| 	} | ||||
| 	for i, c := range cases { | ||||
| 		result := extractServiceName([]byte(c.fileContent)) | ||||
| 		if result != c.expected { | ||||
| 			t.Fatalf("Case %v, expected: %v, got: %v", i, c.expected, result) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user