Last year I talked about private repositories and Gitlab CI as well as our story when we migrated to modules, during that migration (and because of the way we organize our projects) we were forced to start appending .git to our private modules and to use the replace directive in go.mod to explicitly indicate Go the exact location of that repository.
Something like:
require (
    private.gitlab.instance/project/team/service-name v1.0.0
)
replace (
    private.gitlab.instance/project/team/service-name => private.gitlab.instance/project/team/service-name.git v1.0.0
)
That workflow was cumbersome, not idiomatic, and made upgrading our own internal dependencies harder than normal.
However after investigating even more, I realized there was a nicer alternative which involved using:
This required 4 changes:
- Using a Personal Access Token,
- Updating .gitlab-ci.ymlto properly use the new token,
- Updating our Dockerfileto define the.netrcconfiguration, and
- Remove replacedirectives ingo.mod.
Using a Personal Access Token
We specifically need a new token with the following scopes:
- read_api
- repository_access
For security concerns this token will be defined as a new CI/CD environment variable, for that we will define two new variables:
- 
GO_MODULES_USERrepresenting the Gitlab username and
- 
GO_MODULES_PERSONAL_ACCESS_TOKENrepresenting the token we just created.
The reason behind defining a new token instead of using CI_JOB_TOKEN is that this token does not have enough permissions for accessing the Gitlab API used to determine the right repository when calling go mod <xyz> behind the scenes.
  
  
  Updating .gitlab-ci.yml to properly use the new token
Replacing the docker build instruction to pass in the new variables should do it, so from something like this:
docker build \
  --build-arg CI_JOB_TOKEN
We could change it to:
docker build \
  --build-arg GO_MODULES_USER
  --build-arg GO_MODULES_PERSONAL_ACCESS_TOKEN
  
  
  Updating our Dockerfile to define the .netrc configuration
Our Dockerfile will be changed to something like:
FROM golang:1.15.0-alpine3.12
ARG GO_MODULES_USER
ARG GO_MODULES_PERSONAL_ACCESS_TOKEN
WORKDIR /project-name/
RUN go env -w GOPRIVATE="private.gitlab.instance" && \
    echo -e "machine private.gitlab.instance\nlogin ${GO_MODULES_USER}\npassword ${GO_MODULES_PERSONAL_ACCESS_TOKEN}" > ~/.netrc
COPY ["go.mod", "go.sum", "./"]
RUN go mod download
COPY . .
#-
FROM golang:1.15.0-alpine3.12
WORKDIR /project-name/
ENV PATH=/go/bin/:$PATH
COPY --from=0 /project-name/ /project-name/
COPY --from=0 /go/ /go/
Thanks to the multi-stage build we know for sure the credentials are not stored in the final Docker image being built
  
  
  Remove replace directives in go.mod.
Finally the last step would be get rid of those .git replace directives:
require (
    private.gitlab.instance/project/team/service-name v1.0.0
)
And with that we are back to the well known go get/mod workflow!
 


 
    
Top comments (0)