2020年9月4日 星期五

[ 文章收集 ] Evaluating Go's Package Management and Module Systems

 Source From Here

Getting started with Modules
To initialize a project using Modules, enter the command below at the project root:
# go mod init go_dep_ex
go: creating new go.mod: module go_dep_ex

# tree ./
  1. ./  
  2. ├── go.mod  
  3. ├── main.go  
  4. └── vendor  
  5.   
  6. 1 directory, 2 files  
# cat go.mod
module go_dep_ex

go 1.13

The module name doubles as the import path, which allows internal imports to be resolved inside the module. It's also how other projects will import your package (if you're developing a library, for example). Idiomatically, this will be the URL of the repository hosting the code. Note that you don't need to have your project checked into version control or pushed to a remote repository before specifying a module name.

Suppose your project is called example; you can utilize modules in your project using the command below:
$ go mod init github.com/ayoisaiah/example
go: creating new go.mod: module github.com/ayoisaiah/example

The above command will create a go.mod file in the root of your project directory. This will contain the import path for the project and the Go version information. It's the Gemfile equivalent for Go.

Installing dependencies
One of the main reasons Go modules were introduced was to make dependency management a lot easier. Adding a dependency to your project can be done using the go get command just as before:
# go get github.com/joho/godotenv

You can target a specific branch:
# go get github.com/joho/godotenv@master

Or a specific version:
# go get github.com/joho/godotenv@v1.2.0

Or even a specific commit:
# go get github.com/joho/godotenv@d6ee687

Back to our project go_dep_ex, we need packages gin and seelog:
# go get -u github.com/gin-gonic/gin
# go get -u github.com/cihub/seelog
# cat go.mod
  1. module go_dep_ex  
  2.   
  3. go 1.13  
  4.   
  5. require (  
  6.         github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 // indirect  
  7.         github.com/gin-gonic/gin v1.6.3 // indirect  
  8.         github.com/go-playground/validator/v10 v10.3.0 // indirect  
  9.         github.com/golang/protobuf v1.4.2 // indirect  
  10.         github.com/json-iterator/go v1.1.10 // indirect  
  11.         github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect  
  12.         github.com/modern-go/reflect2 v1.0.1 // indirect  
  13.         golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f // indirect  
  14.         google.golang.org/protobuf v1.25.0 // indirect  
  15.         gopkg.in/yaml.v2 v2.3.0 // indirect  
  16. )  
Then main.go can import above two packages now:
main.go
  1. package main  
  2.   
  3. import (  
  4.     "github.com/gin-gonic/gin"  
  5.     "github.com/cihub/seelog"  
  6. )  
  7.   
  8. func main() {  
  9.     r := gin.Default()  
  10.     r.GET("/ping", func(c *gin.Context) {  
  11.         c.JSON(200, gin.H{  
  12.             "message""pong",  
  13.         })  
  14.     })  
  15.     seelog.Info("app run ")  
  16.     r.Run() // listen and serve on 0.0.0.0:8080  
  17.     seelog.Info("app over")  
  18. }  
And you can compile it without problem:
# go build go_dep_ex

If you don't need package seelog anymore and rewrite main.go:
- main.go
  1. package main  
  2.   
  3. import (  
  4.     "github.com/gin-gonic/gin"  
  5.     "fmt"  
  6. )  
  7.   
  8. func main() {  
  9.     r := gin.Default()  
  10.     r.GET("/ping", func(c *gin.Context) {  
  11.         c.JSON(200, gin.H{  
  12.             "message""pong",  
  13.         })  
  14.     })  
  15.     fmt.Println("app run ")  
  16.     r.Run() // listen and serve on 0.0.0.0:8080  
  17.     fmt.Println("app over")  
  18. }  
You can still build it with command "go build go_dep_ex". But you will have one unnecessary package in go.mod. Then you can use below command to resolve it:
# go mod tidy
# cat go.mod
  1. module go_dep_ex  
  2.   
  3. go 1.13  
  4.   
  5. require (  
  6.         github.com/gin-gonic/gin v1.6.3  
  7.         github.com/go-playground/validator/v10 v10.3.0 // indirect  
  8.         github.com/golang/protobuf v1.4.2 // indirect  
  9.         github.com/json-iterator/go v1.1.10 // indirect  
  10.         github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect  
  11.         github.com/modern-go/reflect2 v1.0.1 // indirect  
  12.         golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f // indirect  
  13.         google.golang.org/protobuf v1.25.0 // indirect  
  14.         gopkg.in/yaml.v2 v2.3.0 // indirect  
  15. )  

This command will remove unused dependencies in your project and add any missing ones (for example, if you import a third-party package to your project without fetching it first with go get). Before releasing a new version of your project, and before each commit, you should run the go mod tidy command to ensure your module file is clean and accurate. This file possesses all the required information necessary for reproducible builds.

At this point, there also should be a go.sum file in your project root. It's not a lock file (like Gemfile.lock) but is maintained for the purpose of containing the expected cryptographic hashes of the content of specific module versions. You can think of it as additional verification to ensure that the modules your project depends on do not change unexpectedly, whether for malicious, accidental, or other reasons.

The recorded checksums are retained even if you stop using the module so that you can pick up right where you left off if you start using it again later. For this reason, make sure both your go.mod and go.sum files are checked into version control.

All downloaded modules are cached locally in your $GOPATH/pkg/mod directory by default. If you import a package to your project without downloading it first using go get, the latest tagged version of the module providing that package will be installed automatically and added to your go.mod file when you run go build or go test before your project is compiled.

You can see this in action by adding a new dependency to your project, such as this color package, and using it as shown below:
  1. package main  
  2.   
  3. import (  
  4.     "github.com/gin-gonic/gin"  
  5.     "fmt"  
  6.     "gopkg.in/gookit/color.v1"  
  7. )  
  8.   
  9. func main() {  
  10.     r := gin.Default()  
  11.     r.GET("/ping", func(c *gin.Context) {  
  12.         c.JSON(200, gin.H{  
  13.             "message""pong",  
  14.         })  
  15.     })  
  16.     fmt.Println("app run ")  
  17.     r.Run() // listen and serve on 0.0.0.0:8080  
  18.     color.Red.Println("app over!")  
  19. }  
Running go build will fetch the package and add it to your go.mod file:
# go build go_dep_ex
go: finding gopkg.in/gookit/color.v1 v1.1.6
go: downloading gopkg.in/gookit/color.v1 v1.1.6
go: extracting gopkg.in/gookit/color.v1 v1.1.6


# grep 'color' go.mod
gopkg.in/gookit/color.v1 v1.1.6

Updating dependencies
Go modules use the Semantic Versioning (Semver) system for versioning, which has three parts: major, minor, and patch. A package in version 1.2.3 has 1 as its major version, 2 as its minor version, and 3 as its patch version.

Minor or patch versions
You can use go get -u or go get -u=patch to upgrade a package to its latest minor or patch version, respectively, but you can't do this for major version upgrades. This is because major version updates have different semantics for how they are published and maintained.
$ go get -u gopkg.in/gookit/color.v1
go: gopkg.in/gookit/color.v1 upgrade => v1.1.6

Major versions
The convention for code opting into Go modules is to use a different module path for each new major version. Starting at v2, the path must end in the major version. For example, if the developer of gotdotenv makes a major version release, the module path will change to github.com/joho/godotenv/v2, which is how you'll be able to upgrade to it. The original module path (github.com/joho/godotenv) will continue to refer to v1 of the package.

For example, let's say we're building a CLI app using v1 of this cli package in our project:
  1. package main  
  2.   
  3. import (  
  4.     "os"  
  5.     "github.com/urfave/cli"  
  6. )  
  7.   
  8. func main() {  
  9.     (&cli.App{}).Run(os.Args)  
  10. }  
After building the project and running go mod tidy, our go.mod file should look like this:
$ go build
go: finding module for package github.com/urfave/cli
go: found github.com/urfave/cli in github.com/urfave/cli v1.22.4


$ go mod tidy
$ cat go.mod
  1. module github.com/ayoisaiah/example  
  2.   
  3. go 1.14  
  4.   
  5. require github.com/urfave/cli v1.22.4  

Let's say we want to upgrade to v2 of the package. All you need to do is replace the v1 import path with the v2 import path, as shown below. You should obviously read the documentation for the package you're upgrading, so any necessary changes can be made to the code. In this example, the code stays the same.
  1. package main  
  2.   
  3. import (  
  4.     "os"  
  5.   
  6.     "github.com/urfave/cli/v2"  
  7. )  
  8.   
  9. func main() {  
  10.     (&cli.App{}).Run(os.Args)  
  11. }  
Running go build again will add the v2 package to our import path, alongside v1:
$ cat go.mod
  1. module github.com/ayoisaiah/example  
  2.   
  3. go 1.14  
  4.   
  5. require (  
  6.     github.com/urfave/cli v1.22.4  
  7.     github.com/urfave/cli/v2 v2.2.0  
  8. )  

If you've completed the migration successfully (and all tests pass), you can run go mod tidy to clean up the now unused v1 dependency from your go.mod file. This convention of using different module paths for major versions is known as semantic import versions. Due to this convention, it's possible to use multiple versions of a package simultaneously, such as when performing an incremental migration in a large codebase.

Removing dependencies
To delete a dependency from your project, all you need to do is remove all references to the package from your project, and then run go mod tidy on the command line to clean up your go.mod file. Remember that the cryptographic hash of a package's content will be retained in your go.sum file even after the package is removed.

Vendoring dependencies
As mentioned earlier, all downloaded dependencies for a project are placed in the $GOPATH/pkg/mod directory by default. Vendoring is the act of making a copy of the third-party packages your project depends on and placing them in a vendor directory within your project. This is one way to ensure the stability of your production builds without having to rely on external services.

Here are some other benefits of vendoring:
* You'll be able to use git diff to see the changes when you update a dependency, and this history will be maintained in your git repo.
* If a package suddenly disappears from the internet, you are covered.

If you want to vendor your dependencies in Ruby, you may use the following command:
$ bundle package

Here's how vendoring is achieved in Go:
$ go mod tidy
$ go mod vendor

Running go mod tidy is essential to keep the list of dependencies listed in your go.mod file accurate before vendoring. The second command will create a vendor directory in your project root, and all the third-party dependencies (direct and indirect) required to build your project are copied over to the directory. This folder should be checked into version control. As of Go 1.14, the go command will also verify that your project’s vendor/modules.txt file is consistent with its go.mod file. If not, you may get an error, such as the one shown below, when building your project:
$ go build
  1. go: inconsistent vendoring in /home/ayo/Developer/demo/example:  
  2.         github.com/urfave/cli@v1.22.4: is explicitly required in go.mod, but not marked as explicit in vendor/modules.txt  
  3.         github.com/urfave/cli/v2@v2.2.0: is marked as explicit in vendor/modules.txt, but not explicitly required in go.mod  
  4. run 'go mod vendor' to sync, or use -mod=mod or -mod=readonly to ignore the vendor directory  

To fix this error, run go mod tidy, and then run go mod vendor so that everything is consistent again.

Wrapping Things Up
In this article, we covered the most important concepts you need to know regarding Go modules before adopting them in your project. To recap,
go mod init will initialize modules in your project.
go get adds a new dependency, and you can use go get -u or go get -u=patch to upgrade a dependency to a new minor or patch version.
* go mod tidy cleans up unused dependencies or adds missing dependencies.
go mod vendor copies all third-party dependencies to a vendor folder in your project root.


沒有留言:

張貼留言

[Git 常見問題] error: The following untracked working tree files would be overwritten by merge

  Source From  Here 方案1: // x -----删除忽略文件已经对 git 来说不识别的文件 // d -----删除未被添加到 git 的路径中的文件 // f -----强制运行 #   git clean -d -fx 方案2: 今天在服务器上  gi...