How do I refactor module name in Go? - go

I have a Go module named mymodule, and I'd like to rename it into github.com/hylowaker/awesome-module
Using command go mod edit -module github.com/hylowaker/awesome-module only changes module name in go.mod file, leaving go sources unchanged. I tried Refactor feature in GoLand IDE, but GoLand does not allow renaming with slash(/) characters.
So I had to find and replace every import "mymodule/..." into import "github.com/hylowaker/awesome-module/... from my source files.
Is there a better way to refactor them?

This feature is introduced in GoLand version 2021.1.
You can invoke the Rename refactoring by pressing Shift+F6 on the module name in the go.mod file.

In GoLand just press Ctrl+Shift+R and execute "Replace in Path"
It is safe to perform that in entire project since you only need to change go.mod file and all import clauses

Related

Module XXX found, but does not contain package XXX

Not so familiar with Golang, it's probably a stupid mistake I made... But still, I can't for the life of me figure it out.
So, I got a proto3 file (let's call it file.proto), whose header is as follows:
syntax = "proto3";
package [package_name];
option go_package = "github.com/[user]/[repository]";
And I use protoc:
protoc --go_out=$GOPATH/src --go-grpc_out=$GOPATH/src file.proto
So far so good, I end up with two generated files (file.pb.go and file_grpc.pb.go) inside /go/src/github.com/[user]/[repository]/, and they are defined inside the package [package_name].
Then, the code I'm trying to build has the following import:
import (
"github.com/[user]/[repository]/[package_name]"
)
And I naively thought it would work. However, it produces the following error when running go mod tidy:
go: downloading github.com/[user]/[repository] v0.0.0-20211105185458-d7aab96b7629
go: finding module for package github.com/[user]/[repository]/[package_name]
example/xxx imports
github.com/[user]/[repository]/[package_name]: module github.com/[user]/[repository]#latest found (v0.0.0-20211105185458-d7aab96b7629), but does not contain package github.com/[user]/[repository]/[package_name]
Any idea what I'm doing wrong here? Go version is go1.19 linux/amd64 within Docker (golang:1.19-alpine).
Note: I also tried to only import github.com/[user]/[repository], same issue obviously.
UPDATE:
OK so what I do is that I get the proto file from the git repository that only contains the proto file:
wget https://raw.githubusercontent.com/[user]/[repository]/file.proto
Then I generate go files from that file with protoc:
protoc --go_out=. --go-grpc_out=. file.proto
Right now, in current directory, it looks like:
- directory
| - process.go
| - file.proto
| - github.com
| - [user]
| - [repository]
| - file.pb.go
| - file_grpc.pb.go
In that same directory, I run:
go mod init xxx
go mod tidy
CGO_ENABLED=0 go build process.go
The import directive in process.go is as follows:
import (
"xxx/github.com/[user]/[repository]"
)
Now it looks like it finds it, but still getting a gRPC error, which is weird because nothing changed. I still have to figure out if it comes from the issue above or not. Thanks!
Your question is really a number of questions in one; I'll try to provide some info that will help. The initial issue you had was because
At least one file with the .go extension must be present in a directory for it to be considered a package.
This makes sense because importing github.com/[user]/[repository] would be fairly pointless if that repository does not contain any .go files (i.e. the go compiler could not really do anything with the files).
Your options are:
Copy the output from protoc directly into your project folder and change the package declarations to match your package. If you do this there is no need for any imports.
Copy (or set go_out argument to protoc) the output from protoc into a subfolder of your project. The import path will then be the value of the module declaration in your go.mod plus the path from the folder that the go.mod is in (this is what you have done).
Store the files in a repo (on github or somewhere else). This does not need to be the same repo as your .proto files if you "want it to be agnostic" (note that 2 & 3 can be combined if the generated files will only be used within one code base or the repo is accessible to all users).
Option 1 is simple but its often beneficial to keep the generated code separate (makes it clear what you should not edit and improves editor autocomplete etc).
Option 2 is OK (especially if protoc writes the files directly and you set go_package appropriately). However issues may arise when the generated files will be used in multiple modules (e.g. as part of your customers code) and your repo is private. They will need to change go_package before running protoc (or search/replace the package declarations) and importing other .proto files may not work well.
Option 3 is probably the best approach in most situations because this works with the go tooling. You can create github.com/[user]/goproto (or similar) and put all of your generated code in there. To use this your customers just need to import github.com/[user]/goproto (no need to run protoc etc).
Go Modules/package intro
The go spec does not detail the format of import paths, leaving it up to the implementation:
The interpretation of the ImportPath is implementation-dependent but it is typically a substring of the full file name of the compiled package and may be relative to a repository of installed packages.
As you are using go modules (pretty much the default now) the implementations rules for resolving package paths (synonym of import path) can be summarised as:
Each package within a module is a collection of source files in the same directory that are compiled together. A package path is the module path joined with the subdirectory containing the package (relative to the module root). For example, the module "golang.org/x/net" contains a package in the directory "html". That package’s path is "golang.org/x/net/html".
So if your "module path" (generally the top line in a go.mod) is set to xxx (go mod init xxx) then you would import the package in subfolder github.com/[user]/[repository] with import xxx/github.com/[user]/[repository] (as you have found). If you got rid of the intervening folders and put the files into the [repository] subfolder (directly off your main folder) then it would be import xxx/[repository]
You will note in the examples above that the module names I used are paths to repo (as opposed to the xxx you used in go mod init xxx). This is intentional because it allows the go tooling to find the package when you import it from a different module. For example if you had used go mod init github.com/[user]/[repository] and option go_package = "github.com/[user]/[repository]/myproto";" then the generated files should go into the myproto folder in your project and you import them with import github.com/[user]/[repository]/myproto.
While you do not have to follow this approach I'd highly recommend it (it will save you from a lot of pain!). It can take a while to understand the go way of doing this, but once you do, it works well and makes it very clear where a package is hosted.

vim-go with coc.nvim: how to rename a package?

I have a file work.go opened in vim-go. It's in package oldpackagename.
I want to refactor and rename it to newpackagename. Is there some tooling for that? The reason is, the initial assumptions for oldpackagename do not apply anymore.
I know I can do search and replace stuff, but usually either
GoRename resp.
nmap <leader>rn <Plug>(coc-rename)
work just fine on automatically renaming all occurrences.
But for the package name, I get:
vim-go: [rename] SUCCESS vim-go: cannot rename the identifier at the
requested position
for GoRename and:
[coc.nvim] Error on rename: The element can't be renamed.
for coc.nvim.
Is package renaming different? Do I have to use search/replace or are my configs maybe corrupted?
Under the hood, vim-go uses either gorename or gopls to run :GoRename; the default is now gopls.
Both of these don't support renaming packages:
Open issue for gopls to support package renaming
An explicit mention of "package renaming" as a TODO feature in the gorename source code
For coc.nvim, I'm not sure, but it seems to integrate with gopls, so the same lack of support would apply.
gopls should support package renaming soon (Oct. 2022).
Its integration with vim-go should take advantage of that as soon as the feature is released.
Issue 41567 mentions CL 420958
gopls/internal/lsp: add support for package renaming
Users can now rename packages by performing a rename on the package name in package declarations.
The editor will prompt user with a dialogue text field to change the package name.
This rename feature then do the following:
Rename all the external imports of the renaming package. In case there is renaming conflicts within a file, the feature repeatedly try fresh names constructed by appending new package name with number of try times so far until succeed.
Rename all the internal references to the renaming package from its files.
Rename the directory of the renamed package and update the imports' paths of any packages nested under the renamed directory.
Rename the test package with the new package name and suffix "_test" with the current test package name ends with "_test", or just the new package name otherwise.
However:
Todo:
Add a test for the case when the renaming package's path contains "internal" as a segment.
Allow edit to the whole package path not just only the last segment of the package path
Reject renaming if the renamed subpackages don't belong to the same module with the renaming package
Check the go.mod files in the workspace to see if any replace directives need to be fixed if the renaming affects the locations of any go.mod files

What should I name my Go package if I don't know where I will host it?

I want to write a new Go package. If I type go mod init github.com/user/hello, would that be a correct name, if I end up deciding to host it in another location and not github?
You can rename the module later, so it plays no important part. Keep using that.
To rename a module in go.mod, you may edit it (it's a simple text file), or you may run the command:
go mod edit -module github.com/newuser/newhello
Note that this will only rename the module in go.mod, but if your module consists of multiple packages which import each other, the import statements will not be edited, you have to manually edit those or use a tool to do that.
If you want to test your module before pushing it to a remote repository, see How to use a module that is outside of "GOPATH" in another module?

How do I stop the auto importing of imported item in go outside of my project?

I have my projects that have many packages which import each other and import outside packages. When I make a change to one of my low lever packages, and then push it to git it is fine and works in that section. When I go get it for use in another project that was working perfectly I now get this go get this error:
module declares its path as: github.com/xdg-go/scram
but was required as: github.com/xdg/scram
None of my code uses either of those directly. It looks like it automatically updated some lower external packages and broke things the used to then old import.
How do I either find out the package that is importing the wrong name or stop all auto-updates?
The go.mod file at github.com/xdg/scram declares itself as github.com/xdg-go/scram:
module github.com/xdg-go/scram
go 1.11
require (
github.com/xdg-go/stringprep v1.0.2
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
)
The go.mod file should be updated to reflect the correct import path.
Unfortunately if this module is for you an indirect dependency, the best fix possible is to update whatever project you import that is directly importing it.
When that is not an option, a solution to this error is to clone the problematic repository locally and use the replace directive in your go.mod file:
module mymodule
replace github.com/xdg/stringprep => ../strprep
go 1.16.2
require (
github.com/divjotarora/mgo v0.0.0-20190308170442-1d451d2a3149
)
where ../strprep is where the code of the required module exists in your local machine, relative to the go.mod file of your project.
The downside of this of course is that you have to replicate this palliative fix wherever you plan to go get your modules.
Note also:
divjotarora/mgo is just a random example of a project that imports one of those packages using their old import path.
I'm using xdg/stringprep as an example because I can't find modules that import xdg/scram instead, but apparently it suffers from the same issue
Beside, you can use:
go mod why <package> to find out why a certain package is listed as a dependency of your project
go mod graph to show the full dependency graph. The output is in <package> <requirement> format

How can I force go mod to accept a module that declares its path being different from its go.mod?

When I run go mod tidy, it breaks because a package imported by my project imports another package using path github.com/coreos/bbolt, but when it fetches the package from this path its go.mod says its path is go.etcd.io/bbolt.
The problem is that both the importing package and the imported package are 3rd party packages. I know I could edit the go module cache to fix it, but it would be a real hell fixing it when new versions of these packages become available.
Partial echoed messages are shown below:
github.com/coreos/etcd/client tested by
github.com/coreos/etcd/client.test imports
github.com/coreos/etcd/integration imports
github.com/coreos/etcd/etcdserver imports
github.com/coreos/etcd/mvcc/backend imports
github.com/coreos/bbolt: github.com/coreos/bbolt#v1.3.5: parsing go.mod:
module declares its path as: go.etcd.io/bbolt
but was required as: github.com/coreos/bbolt
So, how can I fix or work around this situation?
You can fix this solution by using the replace directive
Simply add:
replace github.com/coreos/bbolt v1.3.5 => go.etcd.io/bbolt v1.3.5
at the end of your go.mod file
The mismatched path implies that your dependency (github.com/coreos/etcd/mvcc/backend) is written against an old version of the bbolt repository — one that predates commit e65d4d.
I notice that the current go.mod file in the github.com/etcd-io/etcd repo specifies its module path as go.etcd.io/etcd/v3.
So the most robust fix for you is probably to update to that path, which you can do by changing your import statements to refer to the new canonical import path and running go mod tidy to update your dependencies accordingly:
sed -i s,github.com/coreos/etcd,go.etcd.io/etcd/v3,g $(find . -name '*.go')
go mod tidy
Barring that, you could explicitly choose a version of github.com/coreos/bbolt that matches the older import path. I notice that the highest version for that module listed at https://beta.pkg.go.dev/github.com/etcd-io/bbolt?tab=versions is v1.3.3, and v1.3.4 does seem to add a go.mod file with the updated path. So as a fallback, you could try:
go get -d github.com/coreos/bbolt#v1.3.3
The downside to that approach is that v1.3.3 is the end of the line: you wouldn't be able to pull in bug-fixes after that point, because those fixes are all at the go.etcd.io path.
You could also use the replace directive with the command line, as example:
go mod edit -replace github.com/pselle/bar=/Users/pselle/Projects/bar
More info in this article

Resources