Golang中如何管理远程模块的版本

Golang中如何管理远程模块的版本 (我还在Stack Overflow上问了这个问题,但对获得完整答案已经不抱希望了)

引言

我来自 Python/JavaScript 世界,在那里导入模块通常意味着从先前安装的数据中导入。因此,如果我启动一个 Python 程序两次,并且它导入了一个模块,每次我都会得到相同的模块(如果主机上没有特意更新它)——即使在此期间有新的版本可用。

我将我所有的代码都进行容器化(*),从一个空的基础镜像开始,安装模块,然后使用最新版本的模块运行程序(我意识到并理解这样做以及不进行固定版本安装的影响,但这样做是有原因的)。

我现在正在为一个 Go 项目(即 Caddy Web 服务器)准备一个构建流水线(用于一个家庭项目)。我将每晚将其重新构建到一个 Docker 镜像中。主要有两个 Go 命令:

go mod init caddy
go build

源代码从 GitHub 导入了几个模块:

import (
        "github.com/caddyserver/caddy/caddy/caddymain"

        _ "github.com/lucaslorentz/caddy-docker-proxy/plugin"
        _ "github.com/pyed/ipfilter"
        _ "github.com/caddyserver/dnsproviders/ovh"
        _ "github.com/aablinov/caddy-geoip"
        _ "github.com/abiosoft/caddy-git"
)

第一次运行构建时,我看到了很多消息,告诉我插件正在被处理,一些示例行如下:

go: finding github.com/pyed/ipfilter v1.1.4
go: finding github.com/aablinov/caddy-geoip latest

在后续的 Docker 构建过程中,我不再看到这些消息,并且使用了该层的缓存版本。这意味着在该阶段,构建产物没有发生变化。

构建出的二进制文件相同是正常的:在此期间源代码没有改变。让我担心的是,我根本没有看到构建过程。

问题

根据 Stack Overflow 问题中的评论,我的问题是:

  • go mod 会检查远程模块的当前版本吗?还是它只是确保在 go.mod 中定义的级别上有可用的缓存?换句话说——如果有一个新版本,当 go mod 在之前的运行中已经创建了 go.* 文件后,在后续调用时它会获取新版本吗?
  • 如果我删除 go mod 创建的 go.* 文件,go mod 会获取模块的当前版本吗(即使有缓存,如果远程和本地版本不同)?还是它只会查看 import 中的内容,看到缓存中有东西,然后说“好的,我本地有这个模块,所以我完全不需要向 GitHub 检查”?
  • go get 与上述情况如何交互?go get -u 会强制更新缓存的模块吗(如果本地和远程版本不同)?

总的来说:我应该如何构建才能确保使用 import 中模块的最新版本? ← 我在 Stack Overflow 上问了几次这个问题(现在我才意识到我可能应该这样表述我的问题),得到的答案虽然有趣,但从未切中要害(运行 go <这个>go <那个>,构建就会使用模块的最新版本)。有这种方法吗?


(*) 我通过 GitLab 的 CI/CD 构建我的代码,使用我在自己服务器上托管的运行器。它们使用 shell 执行器运行,这意味着从实际角度来看,代码是在主机上构建的,使用该主机的资源。这也意味着 go 命令对所有执行都是通用的(因此该命令可能缓存的所有内容也会被重用)。


更多关于Golang中如何管理远程模块的版本的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

go get -u ./... 从你的模块根目录执行,会升级模块的所有直接和间接依赖,并且现在会排除测试依赖。

(模块 · golang/go Wiki · GitHub)

更多关于Golang中如何管理远程模块的版本的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go中,管理远程模块版本主要通过go.mod文件和go命令的缓存机制实现。以下是针对你问题的具体解答:

1. go mod是否会检查远程模块的当前版本?

默认情况下,go build不会自动检查远程模块的新版本。它会使用go.mod中记录的版本,如果go.mod中指定了版本范围或latest,则会使用缓存的版本。

// go.mod示例
module caddy

go 1.21

require (
    github.com/caddyserver/caddy v1.0.5
    github.com/lucaslorentz/caddy-docker-proxy v0.5.0
)

2. 删除go.*文件的影响

删除go.mod和go.sum文件后,重新运行go mod initgo mod tidy会获取最新版本:

# 删除现有模块文件
rm go.mod go.sum

# 重新初始化并获取最新依赖
go mod init caddy
go mod tidy

但是,如果缓存中已有模块,Go会优先使用缓存。要强制重新下载,需要清理缓存:

# 清理模块缓存
go clean -modcache

# 或者删除特定模块
go clean -modcache -cache

3. go get与版本更新

go get命令用于更新依赖版本:

# 更新所有依赖到最新版本
go get -u ./...

# 更新特定模块
go get -u github.com/caddyserver/caddy

# 更新到最新major版本
go get -u github.com/caddyserver/caddy@latest

# 更新所有依赖,包括间接依赖
go get -u all

4. 确保使用最新版本的构建方案

对于你的Docker构建流水线,推荐以下方案:

方案A:使用go get -u(推荐)

FROM golang:1.21-alpine AS builder

WORKDIR /app

# 复制go.mod和go.sum(如果存在)
COPY go.mod go.sum ./

# 更新所有依赖到最新版本
RUN go get -u ./...

# 复制源代码
COPY . .

# 构建
RUN go build -o caddy

方案B:清理缓存并重新获取

FROM golang:1.21-alpine AS builder

WORKDIR /app

# 清理缓存确保获取最新版本
RUN go clean -modcache

# 复制源代码
COPY . .

# 初始化模块并获取依赖
RUN go mod init caddy && go mod tidy

# 构建
RUN go build -o caddy

方案C:使用特定版本控制

FROM golang:1.21-alpine AS builder

WORKDIR /app

# 复制go.mod和go.sum
COPY go.mod go.sum ./

# 下载依赖(使用go.mod中的版本)
RUN go mod download

# 复制源代码
COPY . .

# 构建
RUN go build -o caddy

5. 完整示例:确保最新版本的Dockerfile

# 使用多阶段构建
FROM golang:1.21-alpine AS builder

# 设置环境变量
ENV GO111MODULE=on \
    CGO_ENABLED=0 \
    GOOS=linux

WORKDIR /build

# 复制模块文件
COPY go.mod go.sum ./

# 强制更新所有依赖到最新版本
RUN go get -u all

# 复制源代码
COPY . .

# 下载依赖并验证
RUN go mod download && go mod verify

# 构建
RUN go build -v -o /app/caddy .

# 最终镜像
FROM alpine:latest
COPY --from=builder /app/caddy /usr/local/bin/caddy
ENTRYPOINT ["caddy"]

6. GitLab CI/CD配置示例

build:
  stage: build
  script:
    - go version
    # 清理缓存并获取最新依赖
    - go clean -modcache
    - go mod init caddy || true
    - go get -u ./...
    - go mod tidy
    - go build -v -o caddy
  artifacts:
    paths:
      - caddy

关键点总结:

  1. 默认行为go build使用go.mod中记录的版本,不检查更新
  2. 强制更新:使用go get -u更新依赖到最新版本
  3. 缓存影响:Go会缓存模块,清理缓存可强制重新下载
  4. 推荐方案:在CI/CD流水线中使用go get -u ./...确保获取最新版本

对于你的夜间构建需求,建议在Dockerfile中使用go get -u all或在CI脚本中执行go get -u ./...,这样可以确保每次构建都获取依赖模块的最新版本。

回到顶部