小熊科技|Go热重载工具 air不好使了:goproxy.cn 的锅?

我使用 VSCode Remote-Containters 作为 golang 开发环境 , 因为生产环境使用的镜像主要是 alpine , 所以开发环境自然而然使用了 golang:alpine , 对应 Dockerfile 的内容如下:
FROM golang:alpineRUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.cloud.tencent.com/g' /etc/apk/repositoriesRUN apk add alpine-sdkRUN go env -w GOPROXY=,directRUN go get github.com/cosmtrek/air如上所示 , 出于众所周知的原因 , 我设置了 GOPROXY , 并且安装了一个名为 air[1] 的工具 , 熟悉 golang 的朋友都知道 , 它是用来实现热重载的 , 本来一切都正常 , 结果突然报错:「Setctty set but Ctty not valid in child」:
小熊科技|Go热重载工具 air不好使了:goproxy.cn 的锅?air
在 air 的 issue[2] 里没找到对应的报告 , 不过在 golang 的 issue[3] 里倒是发现了一些线索:
If tty is going to be open in the child process, then it must have a file descriptor in the child process. When using os/exec.Cmd as the creack/pty code does, tty must be in Stdin or Stdout or Stderr or ExtraFiles. If it isn’t in any of those, then it will be closed when the child process runs. If it is in one of those, that gives you the file descriptor number that you should use in Ctty.
Right now the creack code is setting Ctty to the parent’s file descriptor number. That is working (assuming that it is working) by accident. The correct fix is to ensure that tty is passed to the child somewhere, and set Ctty to the appropriate descriptor number. If it is possible for all of Stdin, Stdout, Stderr to be set, the simplest approach would be to always add tty to ExtraFiles, and set Ctty to 3. That will work for old versions of Go and also for the upcoming 1.15 release.
我只想让 air 正常工作 , 并不想深究工作原理 , 好在里面提到了creack/pty[4] , 而 air 正好依赖[5]它 , 于是顺藤摸瓜找到了对应的issue[6] , 发现此问题是新版 golang 1.15 才出现的 , 并且已经修复[7]了 , 可惜 air 没有升级 pty 版本 , 于是遇到新版 golang 后 , 问题就出来了 。
恰好前几天 Golang 放出来 1.15 的正式版 , 因为我在 Dockerfile 里使用 golang:alpine 作为标签 , 并没有明确版本 , 相当于是 latest , 也就是最新版 1.15 , 所以触发了问题 。 知道了问题的缘由后 , 解决方法就简单了 , 两种方法:

  • 改用 golang:1.14-alpine3.12 这种有版本的标签绕开问题版本 。
  • 使用 go get 命令的时候 , 应该尽可能加上 -u 选项 , 以便能自动升级版本 。
最好的方式莫过于继续使用新版 golang 1.15 , 同时给 go get 加上 -u 选项:
FROM golang:1.15-alpine3.12RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.cloud.tencent.com/g' /etc/apk/repositoriesRUN apk add alpine-sdkRUN go env -w GOPROXY=,directRUN go get -u github.com/cosmtrek/air不过当我们这么干的时候 , 发现我安装的 air 依然有问题 , 为了验证问题 , 我在一个干净的容器里手动安装 , 结果搞出一个匪夷所思的 v1.21.2 的版本来:
shell> go get -u github.com/cosmtrek/airgo: github.com/cosmtrek/air upgrade => v1.21.2我在官网上根本查不到这个版本 , 直觉告诉我可能和 GOPROXY 有关 , 于是禁用后再执行 , 发现一切都正常了 , 好在 goproxy.cn[8] 支持查询各个版本的下载量 , 于是我就查询[9]了一下 v1.21.2 这个匪夷所思的版本 , 结果发现从 2020-08-07 开始一直有数据:


推荐阅读