3

记录VSCode中写Go代码切换Sqlite无CGO依赖版本的过程以及遇到的五个问题

 1 year ago
source link: https://wiki.eryajf.net/pages/74da0a/#%E5%85%B3%E4%BA%8E%E5%89%8D%E7%BD%AE-lint
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

记录VSCode中写Go代码切换Sqlite无CGO依赖版本的过程以及遇到的五个问题

这篇文章的发布时间较早,其中的内容可能已经过时,阅读时请注意甄别。

# 前言

我的 xirang (opens new window) 系统之前引入 sqlite 功能的时候,使用的驱动包是 github.com/mattn/go-sqlite3 v1.14.15,这个库现在在 go 项目当中,引用最多,应用最广,看项目 Used By 达到了 71k,俨然成为这一方面的标准,但有一个最大的问题就是,这是一个 C 语言实现的库,如果要应用这个库,那么你的环境就需要解决这个 CGO 依赖。

如果是在 CentOS 中运行,会看到如下报错:

[error] failed to initialize database, got error Binary was compiled with 'CGO_ENABLED=0', go-sqlite3 requires cgo to work. This is a stub
2023-02-24 12:30:41	PANIC	common/database.go:32	github.com/eryajf/go-ldap-admin/public/common.initSqlite3	failed to connect sqlite3: Binary was compiled with 'CGO_ENABLED=0', go-sqlite3 requires cgo to work. This is a stub
panic: failed to connect sqlite3: Binary was compiled with 'CGO_ENABLED=0', go-sqlite3 requires cgo to work. This is a stub

我的 Mac 没有遇到这个错误,但其他人 Windows 上也有遇到这个错误。

原本引入 sqlite 为了方便的,结果因为这个问题,反而变得麻烦起来,我看了一下 centos 上想要解决这个问题,要耗费相当一番功夫。

于是决定寻找一个相对优雅的解决方案。

# 方案

通过网上检索,我找到了一篇这样的文章:golang不使用cgo的sqlite驱动推荐 (opens new window),这篇文章的作者遇到的困境与我是一致的,他发现了一个无 CGO 版本的库 modernc.org/sqlite,因为这个库是在 gitlab,不太方便,于是作者给搬到了 github,但作者维护的并不及时,也不够规范(没有 go.mod 文件),我便没能应用起来。

后来又来到 gorm 的 issue 区寻找线索,发现了这个问题:Driver for pure Go sqlite implimentation (opens new window),其中一名开发者分享了他维护的一个库:glebarez/sqlite (opens new window),能够与 gorm 完美结合起来使用。

# 实践

说干就干,准备选用这个库来作为 sqlite 的 orm 驱动,具体代码文件内容可见:

点击查看
package common

import (
	"fmt"

	"github.com/eryajf/xirang/config"
	"github.com/eryajf/xirang/model"

	"github.com/glebarez/sqlite"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

// 全局数据库对象
var DB *gorm.DB

// 初始化数据库
func InitDB() {
	switch config.Conf.Database.Driver {
	case "mysql":
		DB = ConnMysql()
	case "sqlite3":
		DB = ConnSqlite()
	}
	dbAutoMigrate()
}

// 自动迁移表结构
func dbAutoMigrate() {
	_ = DB.AutoMigrate(
		&model.User{},
		&model.Role{},
		&model.Group{},
		&model.Menu{},
		&model.Api{},
		&model.OperationLog{},
	)
}

func ConnSqlite() *gorm.DB {
	db, err := gorm.Open(sqlite.Open(config.Conf.Database.Source), &gorm.Config{
		// 禁用外键(指定外键时不会在mysql创建真实的外键约束)
		DisableForeignKeyConstraintWhenMigrating: true,
	})
	if err != nil {
		Log.Panicf("failed to connect sqlite3: %v", err)
	}
	return db
}

func ConnMysql() *gorm.DB {
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&collation=%s&%s",
		config.Conf.Mysql.Username,
		config.Conf.Mysql.Password,
		config.Conf.Mysql.Host,
		config.Conf.Mysql.Port,
		config.Conf.Mysql.Database,
		config.Conf.Mysql.Charset,
		config.Conf.Mysql.Collation,
		config.Conf.Mysql.Query,
	)
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
		// 禁用外键(指定外键时不会在mysql创建真实的外键约束)
		DisableForeignKeyConstraintWhenMigrating: true,
	})
	if err != nil {
		Log.Panicf("初始化mysql数据库异常: %v", err)
	}
	// 开启mysql日志
	if config.Conf.Mysql.LogMode {
		db.Debug()
	}
	Log.Infof("初始化mysql数据库完成! dsn: %s", showDsn)
	return db
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

在包引用的地方,直接把 "gorm.io/driver/sqlite" 改成 "github.com/glebarez/sqlite" 即可,但这只是完成了很小的一步,接下来遇到的问题才是大头。

# 问题集锦

# go 版本

因为依赖库对 go 版本支持在 1.18 以上,而我之前一直用的 1.17,这次看起来不得不升级一下版本了,于是将自己的 go 版本升级为 1.18.10。这个升级为后续的一个问题埋下伏笔,暂时按下不表,且看版本依赖处理时遇到的第一个问题。

# mod 依赖的处理

当我把编码搞定之后,准备直接运行 go mod tidy -v,但却发现各种过不去,这个时候我选择了两种方案来解决这个问题,思路重要,值得记录。

  • 方案一:把 mod 文件清空,然后重新 tidy,让依赖重新自检,但是这样子项目中其他的依赖也大多拉取了最新的版本,反而引出了更多的报错,这不是一个可取的方案。
  • 方案二:如果暂时不知道怎么写,那就先看看别人怎么写的,于是来到 "github.com/glebarez/sqlite" 依赖的 User By 页面,开始寻找能够借鉴学习的项目案例,经过一番查找,找到了: https://github.com/YPSI-SAS/Golang-GORM-tutoriel , 然后自己再把源码拉下来,按照自己的需求进行调配,配置完毕之后,tidy 检查一切没有问题。最后再把两个项目的 mod 文件进行比对,把 orm 相关的版本拷贝到主项目中,之后果然解决了问题。

有时候依赖的自检会受限于当前项目的各种版本,而且也并不能盲目升级,尤其是针对于生产的业务。

# IDE 的一个报错

当我把代码按照规划的方案编码之后,就看到了如下的报错,而且这个报错在好几个地方都出现。

错误信息如下:

8c562458419d0f96.png

实际程序编译以及运行都没有问题,这是因为依赖包大版本升级,但是 Vscode 还应用老的缓存导致,此时将当前窗口关闭,重新打开项目,即可看到这个报错消失。

# golangci-lint 的问题

一切搞定之后,我打算把代码合并到主分支,原以为经过几个小时的折腾,终于可以点下合并就休息了,结果却收到了 action 的 lint 检查失败,详见:action log (opens new window),我于是在本地开始调试 lint。

在本地调试的时候,却出现了如下错误,好吧,我似乎应该把 lint 放在提交代码之前,这是个需要优化的地方。

当我在本地运行 golangci-lint 的时候,看到了如下错误:

$ golangci-lint version
panic: load embedded ruleguard rules: rules/rules.go:13: can't load fmt

goroutine 1 [running]:
github.com/go-critic/go-critic/checkers.init.22()
	/Users/eryajf/software/go/pkg/mod/github.com/go-critic/[email protected]/checkers/embedded_rules.go:47 +0x52c
1
2
3
4
5
6

之后在 golangci-lint 仓库的 issue 区看到了一个说法: https://github.com/golangci/golangci-lint/issues/2374

如果 go 版本升级了,那么这个工具也需要升级,而且还不能一下子直接安装最新版本,因为当下用的 go 版本并不是最新版本的。这个时候来到 golangci-lint 的 release 页面,通过关键字进行搜索,找到支持 1.19 之前的那个版本,就安装这个版本的就可以。

执行如下命令进行安装:

$ go install github.com/golangci/golangci-lint/cmd/[email protected]

然后再来到本地跑 lint 发现就没有什么报错了。接着把 action (opens new window) 文件的 golang 版本以及插件版本都指定为对应合适的版本,再次运行检查就不会报错了。

# 关于前置 lint

我们使用 pre-commit (opens new window) 来完成这一功能。

$ pip3 install pre-commit

查看版本:

$ pre-commit --version
pre-commit 3.1.0

然后在项目根目录添加如下配置文件.pre-commit-config.yaml:

repos:
-   repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v3.1.0
    hooks:
    - id: check-yaml
    - id: trailing-whitespace
    - id: check-added-large-files
-   repo: https://github.com/golangci/golangci-lint # golangci-lint hook repo
    rev: v1.47.3 # golangci-lint hook repo revision
    hooks:
    - id: golangci-lint
      name: golangci-lint
      description: Fast linters runner for Go.
      entry: golangci-lint run --fix
      types: [go]
      language: golang
      pass_filenames: false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

然后运行如下命令将 hooks 载入到 git 配置文件中:

$ pre-commit install
pre-commit installed at .git/hooks/pre-commit

然后将代码某处的 err 错误忽略不做处理,此时提交代码看看是否会检查:

$ gcmsg 'test pre check'
Check Yaml...............................................................Passed
Trim Trailing Whitespace.................................................Passed
Check for added large files..............................................Passed
golangci-lint............................................................Failed
- hook id: golangci-lint
- exit code: 1

config/config.go:30:11: ineffectual assignment to err (ineffassign)
        workDir, err := os.Getwd()
                 ^
1
2
3
4
5
6
7
8
9
10
11

如此就实现了一个简单的提交前的 lint 检查,这里只是先简单抛砖引玉,后续再深入研究一下pre-commit-hooks,是个很好很重要的东西。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK