别让 Go 代码与 GitHub 绑死
Go 语言的一个优点是用代码的获取位置来定义命名空间。例如,如果你的代码托管在 http://github.com/thetrueares/boneclone,只需写 `import "github.com/thetrueares/boneclone"`,Go 就会自动通过 git 拉取依赖。这让开源库的 bug 报告入口一目了然,也让分发 Go 库变得简单,无需依赖中央包管理系统。对很多人来说,代码地址直接等于 Git 托管平台,但这带来一些隐患。建议改用自定义域名,原因如下。
问题所在
使用 Git 托管地址作为代码路径,会让代码与特定托管平台紧耦合。一旦迁移到 GitLab,就必须修改代码,否则拉取的仍是旧版本。由于迁移成本较高,许多团队被锁定在 GitHub 上。这在 Go 社区中几乎成了默认惯例,但确实不合理。
我见过一家公司因更换代码位置的工作量太大且无暇顾及,索性同时使用 GitLab、GitHub 和 Azure DevOps 三个平台,只为简化操作。这也正是我开发 Boneclone 的初衷——支持同时向多个 Git 平台复制骨架代码。结果这家公司不得不为三个托管服务付费,直接增加了成本。
解决方案
使用自定义域名,例如 go.iain.rocks、go.uber.org、go.mongodb.org 等。这样只需修改域名的指向即可。例如,go.iain.rocks/boneclone 指向 github.com/thetrueares/boneclone;如果日后迁移到 GitLab,只需更新解析,最终用户的安装命令保持不变。
我认为,所有使用 Go 的商业开发团队都应为内部库和包使用自定义域名作为命名空间,这是一种避免无意义耦合的简单方式。
以下是我的配置示例,你可以将其用于自己的项目。
Nginx.conf
server {
server_name go.iain.rocks;
root /var/www/go.iain.rocks;
index index.html;
location / {
# 检查查询字符串是否“不包含” 'go-get=1'。
# '~' 用于区分大小写的匹配。
if ($args !~ go-get=1) {
# 这是人类访问者。永久重定向到 GitHub。
# $request_uri 将是路径,例如 /boneclone
return 301 https://github.com/that-guy-iain$request_uri;
}
# 如果是 Go 工具(带有 ?go-get=1),则像之前一样提供 HTML 文件。
try_files $uri $uri/ =404;
}
# --- 来自 Certbot 的 SSL 配置应保留在此处 ---
listen 443 ssl;
listen [::]:443 ssl;
ssl_certificate /etc/letsencrypt/live/go.iain.rocks/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/go.iain.rocks/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}
# 从 HTTP 到 HTTPS 的重定向块也应保留。
server {
listen 80;
listen [::]:80;
server_name go.iain.rocks;
return 301 https://$host$request_uri;
}
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="go-import" content="go.iain.rocks/boneclone git https://github.com/that-guy-iain/boneclone">
<meta name="go-source" content="go.iain.rocks/boneclone https://github.com/that-guy-iain/boneclone https://github.com/that-guy-iain/boneclone/tree/master{/dir} https://github.com/that-guy-iain/boneclone/blob/master{/dir}/{file}#L{line}">
</head>
<body>
</body>
</html>