商汇粹外网资源平台

搜索
查看: 3128|回复: 5

大家现在go项目如何管理的?一个项目是一个gopath下的包,还是一个新的gopa ...

[复制链接]

该用户从未签到

3

主题

12

帖子

33

积分

新手上路

Rank: 1

积分
33
发表于 2022-10-10 10:26:22 | 显示全部楼层 |阅读模式
再用go做开发,在考虑项目结构如何管理,目前有两个组织方式
一:
       一个项目作为一个gopath,使用gvp和gpm管理。
二:
      一个项目作为当前gopath的一个包,使用vendor机制,glide控制版本。

个人观点如下:
一:
     构建清晰,一个项目就是一个gopath,install还是任何命令很方便,go get也没什么关系,唯一遗憾的是官方go get 不只支持版本号,因此需要第三方gpm等工具管理依赖版本,这样管理 任何配置文件,模板等文件直接放在bin目录,代码干净,层次分明。

二:
     无需切换gopath,外部情况下可以安装全局工具,例如gofmt等工具很轻松,无需额外步骤。遗憾的是vendor机制导致gopath目录臃肿。


大家又是如何看待vendor机制的呢?


大家现在go项目如何管理的?一个项目是一个gopath下的包,还是一个新的gopath?
(gvp+gpm VS glide)
回复

使用道具 举报

该用户从未签到

1

主题

14

帖子

66

积分

注册会员

Rank: 2

积分
66
发表于 2022-10-10 10:41:22 | 显示全部楼层
全局一个 gopath 和 每个项目一个单独的 gopath, 各有优缺点.
针对2种选择, 各推荐一个较优实践(欢迎分享更好的做法).

golang 官方文档里的选择:
https://golang.org/doc/code.html对应中文文档:
如何使用Go编程
stackoverflow 上两种选择, 也是一半一半.
Whats a good best practice with Go workspaces?
------------------------ 全局一个 gopath  做法 -------------------------------------
我个人开发环境是设置一个 gopath, 配合 glide 管理.
全局一个 gopath 较优方案:
github管理源码 + glide(go 包管理工具)管理依赖包
glide 介绍:
1.是一个 golang 的包管理工具.
2. 功能类似 Python 的 pip + virtualenv. (个人理解)
3. 可以锁定项目版本, 会在当前工程目录创建 vendor 目录, 存放所有依赖包.(可认为是个虚拟隔离环境)
4. 默认优先从 vendor 下搜索依赖, 找不到, 再从全局 gopath 找.
5. 这样不会污染全局的包(避免各项目依赖包版本冲突问题).
glide 工具:  
GitHub - Masterminds/glide: Package Management for Golang

用法:
Getting StartedGolang 套件管理工具 Glide
glide使用 - golang

如何实践, 可以参考 gogs 项目对 glide 的应用:
GitHub - gogits/gogs: Gogs (Go Git Service) is a painless self-hosted Git service.

------------------------ 每个项目设置一个 gopath 推荐做法 ------------------------
per 项目 per gopath 推荐做法:
使用如下 脚本, 初始化.(非原创)

优点:
1. 自动创建 项目目录结构.
2. 每个项目都是设置临时的 gopath, 避免污染全局的 gopath, 属于随用随初始化, 用完就扔那种.

用法:

保存如下脚本内容到: create_go_proj.sh
执行如下命令, 创建项目初始工程目录:
sh ./create_go_proj.sh myproject


#!/bin/bash########################################################## Module : mk_go_pro.sh# Author : Blair Zhong# Created : 2013.07.23# Modify :# Version : 1.0# Useage : ./mk_go_pro.sh# ./mk_go_pro.sh porject_name# Description: 创建一个go可编译的工程#————————————–————————————–## 根据 Go语言学习园地博客的帖子编写,如有侵权请联系本人# http://blog.studygolang.com/2012/12/go项目的目录结构/## 默认情况下运行本程序,会生成如下目录和文件:# test# ├── bin# ├── install.sh# ├── pkg# └── src# ├── config# │   └── config.go# └── test# └── main.go#————————————–————————————–# 5 directories, 3 files## 其中:# 1, install.sh为安装文件,# 2, config.go为test项目的配置文件# 3, main.go这个你懂的## 生成完毕之后运行进入test目录,运行install.sh会生成如下文件和目录# ├── bin# │   └── test# ├── install.sh# ├── pkg# │   └── darwin_amd64# │   └── config.a# └── src# ├── config# │   └── config.go# └── test# └── main.go# 6 directories, 5 files## 多了两个文件# 1, bin目录下的test,这个是可执行稳健# 2, pkg/darwin_amd64下的config.a,这个是config编译后产生的文件## enjoy it!#########################################################PWD=$(pwd)cd $PWDif [[ "$1" = "" ]]; thenecho "Useage: ./mk_go_pro.sh porject_name"echo -ne "Please input the Porject Name[test]"read Answerif [ "$Answer" = "" ]; thenecho -e "test";PRO_NAME=test;elsePRO_NAME=$Answer;fielsePRO_NAME=$1;fi##########################################################创建目录echo "Init Directory …"mkdir -p $PRO_NAME/binmkdir -p $PRO_NAME/pkgmkdir -p $PRO_NAME/src/configmkdir -p $PRO_NAME/src/$PRO_NAME##########################################################创建 install.sh 文件echo "Create install/install.sh …"cd $PRO_NAMEecho "#!/bin/bash" > install.shecho "if [ ! -f install.sh ]; then" >> install.shecho "echo "install must be run within its container folder" 1>&2" >> install.shecho "exit 1" >> install.shecho "fi" >> install.shecho >> install.shecho "CURDIR=\`pwd\`" >> install.shecho "OLDGOPATH=\"\$GOPATH\"" >> install.shecho "export GOPATH=\"\$CURDIR\"" >> install.shecho >> install.shecho "gofmt -w src" >> install.shecho "go install $PRO_NAME" >> install.shecho "export GOPATH=\"\$OLDGOPATH\"" >> install.shecho >> install.shecho "echo "finished"" >>install.shchmod +x install.sh#创建 config.go 文件echo "Create src/config/config.go …"cd src/configecho package config > config.goecho >> config.goecho func LoadConfig\(\) { >> config.goecho >> config.goecho "}" >> config.go#创建 main.goecho "Create src/$PRO_NAME/main.go …"cd ../$PRO_NAME/echo "package main" > main.goecho >> main.goecho "import (" >> main.goecho " \"config\"" >> main.goecho " \"fmt\"" >> main.goecho ")" >> main.goecho >> main.goecho "func main() {" >> main.goecho " config.LoadConfig()" >> main.goecho " fmt.Println(\"Hello $PRO_NAME!\")" >> main.goecho "}" >> main.goecho "All Done!"

附: 原脚本来源
Go项目的目录结构 —— Go语言学习园地博客
回复

使用道具 举报

该用户从未签到

6

主题

12

帖子

64

积分

注册会员

Rank: 2

积分
64
发表于 2022-10-10 10:56:22 | 显示全部楼层
我现在只学了 go mod 也只会用 go mod,还没遇到什么坑。
2022年回答。
回复

使用道具 举报

该用户从未签到

2

主题

16

帖子

86

积分

注册会员

Rank: 2

积分
86
发表于 2022-10-10 11:11:22 | 显示全部楼层
Go的代码管理从最早的GOPATH模式逐步演进到目前最流行的Go Module模式,目前最新Go 1.18支持工作区模式,可以本地同时开发多个互相依赖的Go Module。

前言

Go 1.18除了引入泛型(generics)、模糊测试(Fuzzing)之外,另外一个重大功能是引入了工作区模式(workspace mode)。
Go官方团队的Beth Brown于2022.04.05在官网上专门写了一篇博文,详细介绍了workspace模式的使用场景和最佳实践。
本人针对官方原文做了一个翻译,以飨读者。同时在本文最后,附上了对workspace模式的入门介绍。
原文翻译

Go官方团队Beth Brown
2022.04.05
Go 1.18新增了工作区模式(workspace mode),让你可以同时跨多个Go Module进行开发。
你可以从download[1]地址下载Go 1.18,release notes[2]有更多关于Go 1.18的变化细节。
工作区(workspaces)

Go 1.18引入的工作区[3]模式,可以让你不用修改每个Go Module的go.mod,就能同时跨多个Go Module进行开发。工作区里的每个Go Module在解析依赖的时候都被当做根Module。
在Go 1.18以前,如果遇到以下场景:Module A新增了一个feature,Module B需要使用Module A的这个新feature,你有2种方案:

  • 发布Module A的修改到代码仓库,Module B更新依赖的Module A的版本即可
  • 修改Module B的go.mod,使用replace指令把对Module A的依赖指向你本地未发布的Module A所在目录。等Module A发布后,在发布Module B的时候,再删除Module B的go.mod文件里的replace指令。
有了Go工作区模式之后,针对上述场景,我们有了更为简单的方案:你可以在工作区目录维护一个go.work文件来管理你的所有依赖。go.work里的use和replace指令会覆盖工作区目录下的每个Go Module的go.mod文件,因此没有必要去修改Go Module的go.mod文件了。
go work init

你可以使用go work init来创建一个workspace,go work init 的语法如下所示:
go work init [moddirs]
moddirs是Go Module所在的本地目录。如果有多个Go Module,就用空格分开。如果go work init后面没有参数,会创建一个空的workspace。
执行go work init后会生成一个go.work文件,go.work里列出了该workspace需要用到的Go Module所在的目录,workspace目录不需要包含你当前正在开发的Go Module代码。
go work use

如果要给workspace新增Go Module,可以使用如下命令:
go work use [-r] moddir
或者手动编辑go work文件。
如果带有-r参数,会递归查找-r后面的路径参数下的所有子目录,把所有包含go.mod文件的子目录都添加到go work文件中。
如果某个Go Module的目录已经被加到go.work里了,后面该目录没有go.mod文件了或者该目录被删除了,那对该目录再次执行go work use命令,该目录的use指令会从go.work文件里自动移除。(注意:自动移除要从Go 1.18正式版本才会生效,Go 1.18beta1版本有bug,自动删除不会生效)
go.work

go.work的语法和go.mod类似,包含如下3个指令:

  • go: go的版本,例如 go 1.18
  • use: 添加一个本地磁盘上的Go Module到workspace的主Module集合里。use后面的参数是go.mod文件所在目录相对于workspace目录的相对路径,例如use ./main。use指令不会添加指定目录的子目录下的Go Module到workspace的主Module集合里。
  • replace: 和go.mod里的 replace指令类似。go.work里的 replace指令可以替换某个Go Module的特定版本或者所有版本的内容。
使用场景和最佳实践

Workspace使用起来很灵活,接下来会介绍最常见的几种使用场景及其最佳实践。
使用场景1

给上游模块新增feature,然后在你的Module里使用这个新feature

  • 为你的workspace(工作区)创建一个目录。
  • Clone一份你要修改的上游模块的代码到本地。
  • 本地修改上游模块的代码,增加新的feature。
  • 在workspace目录运行命令go work init [path-to-upstream-mod-dir]。
  • 为了使用上游模块的新feature,修改你自己的Go Module代码。
  • 在workspace目录运行命令 go work use [path-to-your-module] 。
    go work use 命令会添加你的Go Module的路径到 go.work 文件里:
    go 1.18

    use (
           ./path-to-upstream-mod-dir
           ./path-to-your-module
    )
  • 运行和测试你的Go Module。
  • 发布上游模块的新feature。
  • 发布你自己的Go Module代码。
使用场景2

同一个代码仓库里有多个互相依赖的Go Module
当我们在同一个代码仓库里开发多个互相依赖的Go Module时,我们可以使用go.work,而不是在go.mod里使用replace指令。

  • 为你的workspace(工作区)创建一个目录。
  • Clone仓库里的代码到你本地。代码存放的位置不一定要放在工作区目录下,因为你可以在go.work里使用use指令来指定Module的相对路径。
  • 在工作区目录运行 go work init [path-to-module-one] [path-to-module-two] 命令。
    示例: 你正在开发 example.com/x/tools/groundhog 这个Module,该Module依赖 example.com/x/tools 下的其它Module。
    你Clone仓库里的代码到你本地,然后在工作区目录运行命令 go work init tools tools/groundhog 。
    go.work 文件里的内容如下所示:
    go 1.18

    use (
            ./tools
            ./tools/groundhog
    )

    tools路径下其它Module的本地代码修改都会被 tools/groundhog 直接使用到。
使用场景3:切换依赖配置

如果要测试你开发的代码在不同的本地依赖配置下的场景,你有2种选择:

  • 创建多个workspace,每个workspace使用各自的go.work文件,每个go.work里指定一个版本的路径。
  • 创建一个workspace,在go.work里注释掉你不想要的use指令。
对于创建多个workspace的方案:

  • 为每个workspace创建独立的目录。比如你开发的代码依赖了example.com/util这个Go Module,但是想测试example.com/util2个版本的区别,你可以创建2个workspace目录。
  • 在各自的workspace目录运行 go work init 来初始化workspace。
  • 在各自的workspace目录运行 go work use [path-to-dependency]来添加依赖的Go Module特定版本的目录。
  • 在各自的workspace目录运行 go run [path-to-your-module] 来测试go.work里指定的依赖版本。
对于使用同一个workspace的方案,可以直接编辑go.work文件,修改use指令后面的目录地址即可。
还在使用GOPATH模式存放代码?

也许使用工作区会改变你的想法。 GOPATH 用户可以使用位于其GOPATH 目录底部的go.work 文件来解决他们的依赖关系。 工作区的目标不是完全重建 GOPATH 工作流程,而是创建一个可以共享 GOPATH 的便利和Go Module优点的设置。
为GOPATH创建工作区:

  • 在GOPATH目录的根目录下运行 go work init。
  • 要在工作区中使用本地模块或特定版本作为依赖项,请运行go work use [path-to-module]。
  • 要替换Go Module  go.mod 文件中的现有依赖项,请使用 go work replace [path-to-module]。
  • 要添加 GOPATH 或任何目录中的所有Module,请运行 go work use -r 命令,该命令以递归方式将带有 go.mod 文件的目录添加到你的工作区。 如果一个目录没有 go.mod 文件,或者该目录不再存在,那该目录的 use 指令将从你的 go.work 文件中自动移除。
注意:如果你的工程里没有go.mod文件,但是你想把它加入到workspace里,你需要进入你的工程目录,执行go mod init来添加go.mod,然后运行 go work use [path-to-module] 来把你的工程添加到workspace中。
Workspace命令

除了 go work init 和 go work use,Go 1.18还为Workspace引入了如下命令:

  • go work sync: 把go.work文件里的依赖同步到workspace包含的Module的go.mod文件中。
  • go work edit: 提供了用于修改go.work的命令行接口,主要是给工具或脚本使用。
编译命令以及go mod的一些子命令会检查GOWORK环境变量,用于判断当前go命令是否处于工作区模式下。
如果GOWORK环境变量的值是以.work结尾的文件路径,则启用工作区模式。
要确定目前正在使用哪个go.work文件,可以运行go env GOWORK命令。如果go命令不在工作区模式,那go env GOWORK的输出结果为空。
工作区模式开启后,go.work 文件会被解析,用来确定工作区模式下的3个参数:

  • Go版本
  • workspace下的Module的所在目录
  • 被替换的Module的信息
工作区模式下还可以尝试如下命令:
go work initgo work syncgo work usego listgo buildgo testgo rungo vet
代码编辑器体验优化

对于Go的语言服务器gopls[4]VSCode Go 插件[5] 的升级,我们感到非常兴奋。这可以让我们在兼容LSP(Langugage Server Protocol,语言服务器协议)的代码编辑器上使用Go workspace的体验非常棒。
gopls的 0.8.1[6] 版本为go.work文件引入了代码诊断、代码补全、代码格式化和提示悬浮。你可以在任何兼容LSP的代码编辑器上享受到gopls的新功能。
代码编辑器相关的使用细节


  • 最新的 vscode-go 插件[7] 支持通过编辑器左下角的Go状态栏的快速访问菜单访问go.work文件。


  • GoLand[8] 支持Workspace工作区模式,也有计划为go.work文件新增语法高亮和代码补全功能。
更多关于不同编辑器使用gopls的信息,可以参考 gopls文档[9].
下一步做什么?


  • 下载和安装Go 1.18[10].
  • 尝试跟着我们的Go workspace教程[11]来学习使用workspaces[12]
  • 如果你有关于Go Workspace的任何问题或者建议,请提交issue[13]
  • 阅读 workspace说明文档[14]
  • 探索更多Go命令,包括 go work init, go work sync 等等。
后记

开源地址

文章和示例代码开源在GitHub: Go语言初级、中级和高级教程[15]
公众号:coding进阶。关注公众号可以获取最新Go面试题和技术栈。
个人网站:Jincheng's Blog[16]
知乎:无忌[17]
References


  • https://go.dev/blog/get-familiar-with-workspaces
参考资料

[1]
download: https://go.dev/dl/
[2]
release notes: https://go.dev/doc/go1.18
[3]
工作区: https://go.dev/ref/mod#workspaces
[4]
gopls: https://pkg.go.dev/golang.org/x/tools/gopls
[5]
VSCode Go 插件: https://marketplace.visualstudio.com/items?itemName=golang.go
[6]
0.8.1: https://github.com/golang/tools/releases/tag/gopls%2Fv0.8.1
[7]
vscode-go 插件: https://github.com/golang/vscode-go/releases/tag/v0.32.0
[8]
GoLand: https://www.jetbrains.com/go/
[9]
文档: https://pkg.go.dev/golang.org/x/tools/gopls#readme-editors
[10]
Go 1.18: https://go.dev/dl/
[11]
Go workspace教程: https://go.dev/doc/tutorial/workspaces
[12]
workspaces: https://go.dev/ref/mod#workspaces
[13]
issue: https://github.com/golang/go/issues/new/choose
[14]
workspace说明文档: https://pkg.go.dev/cmd/go#hdr-Workspace_maintenance
[15]
Go语言初级、中级和高级教程: https://github.com/jincheng9/go-tutorial
[16]
Jincheng's Blog: https://jincheng9.github.io/
[17]
无忌: https://www.zhihu.com/people/thucuhkwuji
回复

使用道具 举报

该用户从未签到

13

主题

28

帖子

74

积分

注册会员

Rank: 2

积分
74
发表于 2022-10-10 11:26:22 | 显示全部楼层
试试 Gogradle:
blindpirate/gogradle ,我写的一个包管理工具,提供项目级的GOPATH、版本号,传递性依赖、自动冲突解决、依赖锁定,以及和glide相同的扁平化机制。它不需要预先设置GOPATH,构建过程中会自动把依赖包拖到项目目录下然后启动一个go进程,传递项目级别的GOPATH。
另外它提供了一些自动切换墙内源的特性。
回复

使用道具 举报

该用户从未签到

2

主题

5

帖子

43

积分

新手上路

Rank: 1

积分
43
发表于 2022-10-10 11:41:22 | 显示全部楼层
Go 语言从1.11和1.12开始实现了对包的管理,Golang从1.11版本官方开始推出版本管理工具go module,并且从1.13版本开始,go module将是go语言默认的包依赖管理工具。
1.前提条件
使用go module前需要开启GO111MODULE,需要设置环境变量GO111MODULE=on
2.网络优化
golang1.13以后GOPROXY默认值为https://proxy.golang.org,在国内访问是会限制的,如果没有条件翻墙的话,可以将GOPROXY设置为下面的配置
go env -w GOPROXY=https://goproxy.cn,direct
3.实战
下面通过github上star比较高的go的elastic包为例,github地址为https://github.com/olivere/elastic
先创建一个目录go-elastic,然后进入该目录,执行下面的命令初始化模块
go mod init go-elastic
生成一个go.mod文件,内容如下:
module go-elasticgo 1.17
然后创建主程序文件main.go
package mainimport (    "context"    "fmt"    "github.com/olivere/elastic/v7")func main() {    // 创建ES client用于后续操作ES    client, err := elastic.NewClient(    // 设置ES服务地址,支持多个地址    elastic.SetURL("http://127.0.0.1:9200"), # 此处填写自己的es地址    // 设置基于http base auth验证的账号和密码    elastic.SetBasicAuth("elastic", "elastic123456")) # 此处填写自己的es用户名密码    if err != nil {        // Handle error        fmt.Printf("连接失败: %v\n", err)        } else {        fmt.Println("连接成功")     }}
执行下面的命令,增加缺少的module
go mod tidy
会生成一个名为go.sum的文件,其中包含特定模块版本内容的预期加密哈希,go命令使用go.sum文件确保这些模块的未来下载检索与第一次下载相同的位,以确保项目所依赖的模块不会出现意外更改,无论是出于恶意、意外还是其他原因。 go.mod和go.sum都应检入版本控制。go.sum 不需要手工维护,所以可以不用太关注。
4.go mod常用命令介绍
go mod init        初始化当前文件夹, 创建go.mod文件go mod tidy        增加缺少的module,删除无用的modulego mod vendor      将依赖复制到vendor下go mod verify      校验依赖go mod edit        编辑go.mod文件 go mod graph       打印模块依赖图go mod download    下载依赖的module到本地cache(默认为$GOPATH/pkg/mod目录)
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表