go get {command-line-tool} FAILED
dep가 나와도 해결되지 않는 golang의 문제

go get -u github.com/gohugoio/hugo : FAIL

2018년 3월 9일, 블로그 빌드 스크립트 개선 작업을 했다. 별것도 고치지 않고 푸시했는데 travis-ci에서 빌드 도중 문제가 발생했다.

실패 기록을 뜯어보고 이유를 찾았다.

$ go get -u github.com/gohugoio/hugo
# github.com/gohugoio/hugo/output
../../gohugoio/hugo/output/docshelper.go:4:2: imported and not used: "fmt"

golang은 사용하지 않는 import가 있으면 컴파일 에러로 취급한다. 사용되지 않은 import fmt가 코드에 끼어있어서 컴파일 에러가 나면서 hugo 설치에 실패한거 같더라.

그래서 코드를 뜯어봤다. 내가 블로그를 빌드하기 한 시간 전에 hugo에 올라온 커밋이 있었다. 이것이 컴파일 에러의 원인이었다. (고작 한시간 차이로 엿먹을줄이야!) 개발자가 실수로 컴파일되지 않는 코드를 master에 그대로 올렸고 이로인해서 hugo가 설치되지 않은거였다.

hugo를 git master branch가 아닌 안정버전에서 받아서 설치하는게 가능하다면 문제를 해결할 수 있을것이다.

dep -> FAIL

원인은 찾았으니 우회하면 된다. 얼마전에 dep라는 golang의 의존성 관리 프로그램을 배웠다. python의 pip, node.js의 npm, rust의 cargo같은 프로그램이다. 이걸 사용해서 특정 버전의 라이브러리르 설치할수 있다. dep를 사용해서 안정버전 hugo를 설치하면 될거같다.

dep를 사용하는 방법은 다음과 같다.

  1. dep ensure -add github.com/foo/bar를 설치한다.
  2. 내가 짜는 golang 소스에서 github.com/foo/bar를 사용한다
  3. 새로운 환경에서는 dep ensure를 이용해서 관련된 패키지를 설치한다.

이 방식대로 했다.

  1. dep ensure -add github.com/gohugoio/hugo로 hugo를 설치한다
  2. dep ensure로 의존성 걸린 패키지를 다시 설치해본다
  3. hugo가 설치되지 않는다.
  4. ????

문제가 발생했다. hugo는 dep ensure -add로 추가를 해도 dep ensure를 하면 사라진다. 근데 dep ensure를 할수 없으면 의미가 없다. 그래서 dep ensure의 설명을 읽어봤다.

$ dep ensure -examples
dep ensure

    Solve the project's dependency graph, and place all dependencies in the
    vendor folder. If a dependency is in the lock file, use the version
    specified there. Otherwise, use the most recent version that can satisfy the
    constraints in the manifest file.
...
dep ensure -add github.com/pkg/foo github.com/pkg/foo/bar

    Introduce one or more dependencies, at their newest version, ensuring that
    specific packages are present in Gopkg.lock and vendor/. Also, append a
    corresponding constraint to Gopkg.toml.

    Note: packages introduced in this way will disappear on the next "dep
    ensure" if an import statement is not added first.
...

dep ensure는 내 golang 소스를 읽어서 설치한 패키지가 진짜로 import 되어있는지 확인한다. 만약 import한적 없는 패키지라면 쓸모없는 패키지일테니 목록에서 지워버린다. 불필요한 패키지를 목록에서 제거하면 빌드에는 영향을 주지 않으면서 패키지를 받는 속도가 빨라지니 의도는 이해된다.

하지만 hugo는 내 프로젝트에 import되는 라이브러리가 아니다. 이것을 cli툴로 쓰고 싶은거다. cli툴을 dep로 설치하고 싶을때는 어떻게 해야되는가? dep 문서를 읽으면 답을 찾을 수 있다.

Dep is a tool intended primarily for use by developers, to support the work of actually writing and shipping code. It is not intended for end users who are installing Go software - that’s what go get does.

dep는 그런 목적으로 만든게 아니니까 go get을 그냥 쓰랜다.

go get -> FAIL

go get으로 특정 버전의 패키지를 깔수 있다면 문제를 해결할수 있을것이다. 하지만 그게 안된다. go get에는 그런 기능이 없다.

go get는 저장소에서 소스를 받아서 그대로 설치한다. go 1.5까지는 master 브렌치에서 소스를 받았다. 그래서 master branch가 없는 저장소는 go get 하면 에러났다. (당해봐서 안다) go 1.6부터는 default 브렌치에서 소스를 받는다. 오늘 사용한 버전은 go 1.10인데 동작은 바뀐게 없다.

go get으로 특정 브렌치나 커밋, 태그의 소스를 받는 방법이 있지 않을까? 없다. 신기하게도 그런 기능은 아직도 추가가 되지 않았다. 나중에는 생길지 모르지만 go 1.10까지는 없다.

남들 하는거 보니까 라이브러리의 특정 태그를 쓰고싶을때는 이런 방법을 쓰더라.

  1. go get github.com/foo/bar
  2. $GOPATH/src/github/foo/bar로 이동
  3. golang 패키지 설치는 별거 아니다. 그냥 git clone 받은거다.
  4. git checkout i-need-develop-branch

golang 패키지는 git clone 받은 것과 동일하기 떄문에 go get을 사용하지 않는 경우도 있다. go get으로 받아서 패키지가 설치될 위치에 맞춰서 git clone 받아도 똑같이 작동한다.

summary

go getdep에 엿먹어서 그냥 써본 글이다.

몇가지 우회법은 알고 있지만 그거 적용해서 문제 수정후 hugo가 복구되었을때 빌드 스크립트를 되돌리는건 귀찮다. hugo 개발자가 빌드 박살난걸 깨닫고 버그를 수정하는게 더 빠를거같다. golang의 패키지 관리에서 엿먹은게 이번이 처음이 아니다. 그럼에도 golang만의 장점이 좋아서 쓰고있었다. 하지만 오늘 당한건 golang을 탈주할 계기가 될거같다.


comments powered by Disqus