How to update imports when they change location - go

I see that in Go you can import packages directly from Github like:
import "github.com/MakeNowJust/heredoc"
I understand that the path I am seeing in import line is not an URL, but only the path the package is located in (normally relative to $GOROOT/src/pkg or $GOPATH/src). So the package heredoc is most probably located in the directory $GOPATH/src/github.com/MakeNowJust/heredoc.
Now let's say that the package developer decided to migrate the code repo to Bitbucket. So now the library URL is bitbucket.com/muchMoreCoolerName/heredoc. He also added some new features to the code repo.
My question is how will you get the updated code?
The only solution I can think of is changing all the imports to new URL and doing go get again. But changing the code for library update seems a little inconvenient.

If you just use go get and then import, there is no way around it, you will have to update the import paths to get the new code. However, if you use vendoring(a technique to keep your dependency with your code and also distribute it with them) you would be isolated from that move at least until you update. When you want to update, you could use a vendor functionality to keep the old import path but to sync with the other repo.
Frankly, I'd still use vendoring in anyway and just do a search and replace for the old import path when I decide to update, this is not that hard.
EDIT You can also use dep to manage dependencies if you haven't transitioned to modules yet.

Related

What is the correct setting for GOPATH?

I was trying to follow a tutorial on using aws with go. When I gave the command "go get github.com/aws/aws-sdk-go/aws", I still got failure to import. I was left wondering if the "go get" succeeded or not.
Following the guidance provided in the answers here, I updated my GOPATH variable and now the import succeeds.
Your hello.go should start something like this:
package main
import (
"github.com/aws/aws-sdk-go/aws"
)
See this example in the playground. So when importing a remote module use the full path in the import statement.
Re another question you asked in the comments (and I mentioned in comments to another of your questions) - go mod init initialises a module. See this article for information. When using modules, GOPATH is no longer used for resolving imports (see this article). So basically GOPATHis the old way of doing things; go modules is the new way (it solves a lot of issues but having the two approaches can be confusing to someone new to the language because some tutorials assume GOPATH and others use modules).
For completeness you have other questions about environmental variables. I think this may be due to a misunderstanding about how these work. When you enter export GOPATH=XXX in a terminal session that will update the environment within that session (that is that terminal window only; it will not have any impact on any other sessions you have open, including vscode). If you want to set a system wide environmental variable then you need to update a configuration file (often ~/.bashrc but this depends upon your OS see this article for info). After doing that restart the applications (or ideally youre PC) to pick up the new setting.
When you run go get github.com/aws/aws-sdk-go/aws; go tool will download the package into $GOPATH/src/github.com/aws/aws-sdk-go/aws directory.
That is why you need to import package by its full name (which is same as path to files under $GOPATH/src directory:
import "github.com/aws/aws-sdk-go/aws"
You can find more about how source code is organized under GOPATH and how imports work here and specifically about remote (github) imports here.
NOTE: GOPATH resolution works only when your project doesn't use modules (you don't have go.mod file). If you're following a tutorial and it doesn't mention use of modules above should work fine.

managing hardcoded import paths

In Go, it is common that some packages are versioned. So a program might look like this:
package main
import (
"github.com/go-gl/gl/v3.3-core/gl"
"github.com/go-gl/glfw/v3.2/glfw"
)
// ... do stuff
Sometimes, I might want to update the version of glfw. Lets imagine GLFW 3.3 bindings come to Go and I want to update from 3.2.
I might have multiple Go files in a project all using glfw. I don't want to go into each of them and update the version of the import by hand. Ideally I wouldn't be copying that long path around, either, and I could define it in one place per project.
Maybe I could write a script to find+replace "github.com/go-gl/glfw/v3.2/glfw"
Maybe I could template the file with Genny
Maybe I could create a symlink inside the root Go path "glfw" -> "github.com/go-gl/glfw/v3.2/glfw", update it when changing version, and just use import "glfw"
but this information then lives "outside" the project, so no-one cloning my project knows what version to use
but this is a global change and I might have multiple projects which want to depend on different versions
Ideally I would be able to do something like this in each source file:
package main
import (
$gl
$glfw
)
And in some project-level dot file, something like:
gl=github.com/go-gl/gl/v3.3-core/gl
glfw=github.com/go-gl/glfw/v3.2/glfw
Or, a command-line argument attached to go build defining constants that could look something like:
go build -Dgl=github.com/go-gl/gl/v3.3-core/gl -Dglfw=github.com/go-gl/glfw/v3.2/glfw
How is everyone else handling this currently?
See github.com/golang/go/wiki/Modules for the recommended way of managing package versions.

how to organize GO project packages if it will hosted on different repos (GitHub & SourceForge)

Say I want to create a project and host it on GitHub,I HAVE TO create project struct like this:
src/github.com/user/
myproject/
main.go
util/
fileutil.go
and in the main.go ,I have to write the import as:
import github.com/user/myproject/util/fileutil
AND now I also want to host this project onto SourceForge, should I copy the whole project and modify the path ?? it seems not so good enough. Is there any other way to do that ? What I need is just create my project under src folder, and can be hosted to any repo as I wish, without changing the packages.
If developing an app and not a library
If you don't plan to have other projects "import" your own project (this is, if you are implementing an application and not a library), it might be possible for you to avoid any reference to github.com in your local paths.
Note: this is not the recommended approach, but from my tests it does work, you can compile, run and test your code if you structure it this way.
You can create your project structure as follows:
src/
myproject/
main.go
util/
fileutil.go
Then you can import like this:
import "myproject/util/fileutil"
You can then host the contents of the myproject folder anywhere without changing paths within the project files.
If developing a library
If you are actually developing a library others will be able to import, then it gets more complicated since those projects will actually need a full path to import your project.
You can create a "vanity" import path, like myproject.io/... by using the meta tag like described here:
https://golang.org/cmd/go/#hdr-Remote_import_paths
That way, when the go tool queries your myproject.io/ pages, you should respond with a header like this:
<meta name="go-import" content="myproject.io git https://github.com/user/myproject">
And then change that header you return if you decide to move away from github.
Note that this does not prevent users to import your project directly from github, if you want to avoid that you need to use the technique described in the canonical import path spec:
https://golang.org/doc/go1.4#canonicalimports

how to handle go import absolute paths and github forks?

There are plenty of questions around this, including why you shouldn't use import "./my/path" and why it only works because some legacy go code requires it.
If this is correct, how do you handle encapsulation of a project and by extension github forks? In every other lang, I can do a github fork of a project, or git clone, and everything is encapsulated there. How do I get the same behaviour out of a go project?
Simple example using the go "hello world" example.
hello.go
package main
import ("fmt"
"github.com/golang/examples/stringutil")
func main() {
fmt.Printf(stringutil.Reverse("hello, world")+"\n")
}
The above works great. But if I want to use my own stringutil which is in a subdirectory and will compile to a single binary, I still need the complete path:
package main
import ("fmt"
"github.com/myrepo/examples/util/stringutil")
func main() {
fmt.Printf(stringutil.Reverse("hello, world")+"\n")
}
Now, if someone copies or forks my repo, it has a direct dependency on "github.com/myrepo/", even though this is used entirely internally!
What if there are 20 different files that import utils/? I need to change each one each time someone forks? That is a lot of extraneous changes and a nonsensical git commit.
What am I missing here? Why are relative paths such a bad thing? How do I fork a project that refers to its own subsidiary directories (and their packages) without changing dozens of files?
As for the reasoning behind not allowing relative imports, you can read this discussion for some perspective: https://groups.google.com/forum/#!msg/golang-nuts/n9d8RzVnadk/07f9RDlwLsYJ
Personally I'd rather have them enabled, at least for internal imports, exactly for the reason you are describing.
Now, how to deal with the situation?
If your fork is just a small fix from another project that will probably be accepted as a PR soon - just manually edit the git remotes for it to refer to your own git repo and not the original one. If you're using a vendoring solution like godep, it will work smoothly since saving it will just vendor your forked code, and go get is never used directly.
If your fork is a big change and you intend to remain forked, rewrite all the import paths. You can automate it with sed or you can use gofmt -r that supports rewriting of the code being formatted.
[EDIT] I also found this tool which is designed to help with that situation: https://github.com/rogpeppe/govers
I've done both 1 and 2 - when I just had a small bugfix to some library I just changed the remote and verndored it. When I actually forked a library without intent of merging my changes back, I changed all the import paths and continued to use my repo only.
I can also think of an addition to vendoring tools allowing automation of this stuff, but I don't think any of them support it currently.

How could I copy a package from the golang source to hack on

I want to basically copy the parse and template packages from the golang source: http://golang.org/pkg/text/
I want to be able to just play around wit the code and hack on it for fun and learning etc.
Can someone tell me how to go about doing this?
Could I just copy the package folders and then somehow move it to my own package like:
$GOPATH/src/github/user/parse
$GOPATH/src/github/user/template
Is this a safe way to do this? How will it build?
Yes, you can just copy the folder like you've described.
When you import it in your application, you just use your new import path:
import (
"github.com/user/parse"
"github.com/user/template"
)
It is completely safe to do that. Just Go ahead and code!
Ps.
I assumed your path would actually be:
$GOPATH/src/github.com/user/parse
since the domain (.com) will also be included in the go get path if you use github to host your package.
Ps2.
Remember to update the imports in the github.com/user/template package to refer to your new parse package instead of the original one.

Resources