golang with visualstudio.com git repos
The opening
By default golang does not work with git repos hosted by visualstudio.com.
This is due to:
-
Authentication is required
-
golang is not aware of visualstudio.com like it is of github.
-
URL paths to repos in visualstudio.com are hostile to golang.
In particular, the fact that the repo URLs contain
_git
makes them unusable from go even if you overcome the other issues.
How things should work
Git repos at visualstudio.com
Setup:
-
You are a user of visualstudio.com.
-
Your git repositories are hosted by ACCOUNT.visualstudio.com
-
The path to a repository looks like this:
https://ACCOUNT.visualstudio.com/COLLECTION/PROJECT/_git/REPO
- The repositories require authentication.
golang tooling
We would like to use standard golang tools with our repos. For example:
go get https://ACCOUNT.visualstudio.com/COLLECTION/PROJECT/_git/REPO_A
We expect this to fetch and things into:
$GOPATH/src/ACCOUNT.visualstudio.com/COLLECTION/PROJECT/_git/REPO_A
We would like to list packages there and install things like so:
# this should work
go list ACCOUNT.visualstudio.com/...
# this should work too
go install ACCOUNT.visualstudio.com/...
golang programming
In our code, we expect we can reference the packages from visualstudio.com:
// This should in theory work
import (
"ACCOUNT.visualstudio.com/COLLECTION/PROJECT/_git/REPO_A/SOME_PACKAGE"
)
// And using the package should work too
SOME_PACKAGE.someFunction()
Problem
None of the above expected behaviour works.
Authentication
This can be solved by adding these lines to global gitconfig:
[url "https://USER:ACCESS_TOKEN@ACCOUNT.visualstudio.com/"]
insteadOf = https://ACCOUNT.visualstudio.com/
Now you can use git commands without password and all is good.
git clone https://ACCOUNT.visualstudio.com/COLLECTION/PROJECT/_git/REPO
Using go get
This still does not work after authentication fix because go get
does not know
the URL is a git repo! We get mysterious messages about import paths etc.
How do we tell go
tools it’s a git repo? One way is to add .git
to the end
of the URL.
go get https://ACCOUNT.visualstudio.com/COLLECTION/PROJECT/_git/REPO.git
This does seem to work initially, and we indeed have things in
$GOPATH/src
as expected. Except there are other issues.
Using go list
and programming
The real problem appears when we try to list packages:
go list ACCOUNT.visualstudio.com/...
We get an error that nothing is found.
Nothing works when we also try to reference the package in the source code:
// Does not work!
import (
"ACCOUNT.visualstudio.com/COLLECTION/PROJECT/_git/REPO_A.git/SOME_PACKAGE"
)
That _git
part is the problem
Why?? Turns out golang
ignores everything that start with a dot .
and underscore _
.
How unlucky that our visualstudio.com URLs have _git
in them!
Other annoyances
The URL for visualstudio.com is long and ugly. The need to use .git
at the end
is counter-intuitive and also can potentially cause issues.
Solution
The <meta>
tag
There is a mysterious mention at https://golang.org/cmd/go/:
If the import path is not a known code hosting site and also lacks a version control qualifier, the go tool attempts to fetch the import over https/http and looks for a tag in the document’s HTML <head>.
The meta tag has the form:
<meta name="go-import" content="import-prefix vcs repo-root">
Interesting!
Let’s use some curl:
curl -i http://golang.org/x/tools
This gives us:
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
X-Cloud-Trace-Context: 6304c66d6794084c44551614bfd9ac7a
Date: Wed, 01 Feb 2017 11:04:09 GMT
Server: Google Frontend
Content-Length: 588
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta name="go-import" content="golang.org/x/tools git https://go.googlesource.com/tools">
<meta name="go-source" content="golang.org/x/tools https://github.com/golang/tools/ https://github.com/golang/tools/tree/master{/dir} https://github.com/golang/tools/blob/master{/dir}/{file}#L{line}">
<meta http-equiv="refresh" content="0; url=https://godoc.org/golang.org/x/tools">
</head>
<body>
Nothing to see here; <a href="https://godoc.org/golang.org/x/tools">move along</a>.
</body>
</html>
And hereby lies the solution.
How we fix it
The solution is to have a server which will respond with HTML like above and point it to the visualstudio.com URL.
This resolves two problems:
- tells golang that our repo is a git repo
- removes the
_git
which prevents us to use packages
This does not solve authentication issues as such but see the gitconfig
workaround.
Imagine we host this web site at vsts.golang.io
Then we could use these URLs to reference our visualstudio.com repos:
# this will fetch from ACCOUNT.visualstudio.com/COLLECTION/PROJECT/_git/REPO
go get vsts.golang.io/ACCOUNT/COLLECTION/PROJECT/REPO
go get vsts.golang.io/ACCOUNT/COLLECTION/PROJECT/REPO/PACKAGE
In our code, the import path would look like this:
// This will work
import (
"vsts.golang.io/ACCOUNT/COLLECTION/PROJECT/REPO/PACKAGE"
)
Example responses from our web server
curl localhost.git/microsoft/DefaultCollection/Personal/philip-test-goapp
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta name="go-import" content="localhost.git/microsoft/DefaultCollection/Personal/philip-test-goapp git https://microsoft.visualstudio.com/DefaultCollection/Personal/_git/philip-test-goapp">
</head>
<body>
Nothing to see here!
</body>
</html>
curl localhost.git/oracle/DefaultCollection/Personal/philip-test-goapp/some_package
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta name="go-import" content="localhost.git/oracle/DefaultCollection/Personal/philip-test-goapp/some_package git https://oracle.visualstudio.com/DefaultCollection/Personal/_git/philip-test-goapp">
</head>
<body>
Nothing to see here!
</body>
</html>
One last thing
For completely transparent go tooling the web app must support HTTPS.
The implementation
See https://gist.github.com/ppanyukov/d3fbc5d0e85569146a264a994d032b0d for
sample implementation in go
.