How to resolve conflicting go module dependencies when a top-level module and one of its sub-modules are separately imported as separate versions?
Asked Answered
D

3

10

I have two dependencies in my project.

go.mod:

module github.com/test-org/test-repo

go 1.12

require (
    github.com/foo/bar v1.0.0
    github.com/raz/mataz v1.0.0
)

After running go mod download, those two dependencies result in two different versions of a github.com/shared/dependency to be downloaded. The interesting thing is that the github.com/shared/dependency contains sub-modules, e.g.:

dependency
  -- go.mod
  -- api
      -- go.mod

Inspecting the downloaded modules shows two versions are downloaded to my local machine:

ls ${GOPATH}/pkg/mod/github.com/shared:

[dir] dependency    [dir] [email protected]

ls ${GOPATH}/pkg/mod/github.com/shared/dependency:

[dir] [email protected]

Looking at the contents of these directories:

${GOPATH}/pkg/mod/github.com/shared/[email protected]:

The file contents of the whole repo in v1.1.0, including the api folder with its own go.mod file.

${GOPATH}/pkg/mod/github.com/shared/dependency/[email protected]:

The file contents of the api folder of the repo in v1.2.0, including the go.mod file.


Finally, I have a .go file in my test-repo with the following setup:

package test-package

import (
    "github.com/foo/bar"
)

func MyFunc() {...bar.NewBar()...}

When I try to run a test of MyFunc (that exists elsewhere), I get an unknown import path...ambiguous import... error message. e.g.

go test github.com/test-org/test-repo/test-package -test.run=TestMyFunc -v:

unknown import path "github.com/shared/dependency/api": ambiguous import: found github.com/shared/dependency/api in multiple modules:
    github.com/shared/dependency v1.1.0 (${GOPATH}/pkg/mod/github.com/shared/[email protected]/api)
    github.com/shared/dependency v1.2.0 (${GOPATH}/pkg/mod/github.com/shared/dependency/[email protected])

The error points to the import line of the .go file importing github.com/shared/dependency/api in the github.com/foo/bar repo. It doesn't know which api to choose in my local ${GOPATH}/pkg/mod folder, given the two versions available:

  1. ${GOPATH}/pkg/mod/github.com/shared/[email protected]/api
  2. ${GOPATH}/pkg/mod/github.com/shared/dependency/[email protected]

Is there any way that I can make that go test call work (solve the dependency conflicts)? Neither of my two dependencies explicitly call out downloading the full shared/[email protected], but for some reason it gets pulled in. If that weren't there, it would seemingly fix the issue.

Decaliter answered 16/9, 2019 at 7:44 Comment(9)
Packages with major version > 1 must be imported with an import path ending in "/v2". You cannot have the same package (identified by its import path) in two different versions. That's why a different version needs a different import path: Append v2. Consolt the Go Modules wiki which explains all this. (Possible duplicate).Motivation
The Go modules concept builds on that modules must be semantically versioned according to semver. And if one of your requirement is v1.12 and other is v1.0, that is the same major version, thus the v1.12 must be compatible with v1.0, and so the go tool will choose v1.12 which should work. If you need a different major versions, starting with v2 it must be part of the import path, thus it will count as a different package / dependency, and all different major version will be included separately.Wingard
@Volker, I've changed the versioning in the question, as my issue isn't actually with v1 vs. v2, but with v0 vs. v1. So your suggestion doesn't apply. I was unaware of the v2+ specifics you mentioned, though. Good to know.Decaliter
You cannot have v0.0.0 and v1.0.0 of the same package. Dead simple. v0.0.0 is not even valid but just used for compatibility. Sometimes all you can do is fix upstream.Motivation
@Motivation Ok, so if one of my dependencies requires a v0.x.x version of the package, and the other dependency requires a v1.x.x version, the only solution is to change the requirement that one of those dependencies has in its go.mod file? Is that correct? Initially with the go modules thing, I had the impression that sub-modules might be able to get away with different versions. For example, if I made a second go.mod file in my repo for my package that needed the other version of the dependency, I thought that might work, but I could be way off course with what go modules are capable of.Decaliter
@Wingard An interesting thing is that I'm actually dealing with two errors of the same format described in the question. One is with a v0.x.x vs. v1.x.x, but the other is with a v1.4.x vs. a v1.1.x. Thus, the expected go tool behavior of automatically selecting doesn't appear to be working in this case. Note that this might be an edge case, as it involves importing a sub-module for one of those two versions (v1.4.x and v1.1.x).Decaliter
To again remove the possibility of people coming to solutions to my question that do not fix the errors, I am going to change the versioning in the question to be only differences in minor versioning.Decaliter
Well, semantically a module cannot require v0.x.y of a dependency as this means something like "I need the dependency in a not-released version. Go goes strictly Semver here (see semver.org/#spec-item-4). If you really are forced to do this: Ask on golang-nuts.Motivation
Go modules resolves version according to minimum version selection. If A has transitive dependencies on B in version 1.2 and 1.4 Then version 1.4 will be chosen and you will not see an error.Motivation
D
13

The issue turned out to be that one of the dependencies was referencing a version of the github.com/shared/dependency/api that was pre-go-modules.

This caused the go tooling to have a module reference to the github.com/shared/dependency/api sub-module, but also a black box import of the entire github.com/shared/dependency repo for the pre-go-modules version. In this example, it would mean that v1.2.0 had go modules (had a go.mod file), and v1.1.0 did not.

Adding the following line to my go.mod file was able to fix the issue, and this solution worked with multiple dependencies I had with such conflicts:

replace (
    github.com/shared/dependency => github.com/shared/dependency v1.2.0
)

Note that this solution only works because we are forcing references to the shared dependency to use go-module-enabled versions (v1.2.0+).

Decaliter answered 16/9, 2019 at 23:11 Comment(0)
G
2

For future reference, I found that running go get github.com/shared/dependency/api@latest seemed to work when the replace method above did not.

Presumably this is forcing both modules to use the latest version of the dependency.

Glarus answered 7/3, 2023 at 20:44 Comment(0)
E
-1

I Simply solved issues of ambiguous import: found package time in multiple modules:

Just delete the mod file and go mod init again with some different name that not matches with any existing module name.

go mod init time - INCORRECT

go mod init gotime - CORRECT

Electorate answered 12/4, 2023 at 8:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.