Skip to content

fix(gomod): auto-append +incompatible for non-module-path major versions#1012

Open
kartikjoshi21 wants to merge 2 commits intoproject-dalec:mainfrom
kartikjoshi21:kartikjoshi21/gomod-incompatible-replace
Open

fix(gomod): auto-append +incompatible for non-module-path major versions#1012
kartikjoshi21 wants to merge 2 commits intoproject-dalec:mainfrom
kartikjoshi21:kartikjoshi21/gomod-incompatible-replace

Conversation

@kartikjoshi21
Copy link
Copy Markdown
Member

Summary

Normalize gomod replacement targets that use a v2+ semver on a module path
without a /vN suffix by automatically appending +incompatible.

This prevents builds from failing for replace targets such as:

  • github.com/docker/cli@v29.2.0

which should be normalized to:

  • github.com/docker/cli@v29.2.0+incompatible

What changed

  • updated GomodReplace.goModEditArg() to normalize replacement targets

Why

Go module versioning requires /vN suffixes for major versions 2 and above.
For repositories that do not follow semantic import versioning, +incompatible
is required. This change makes Dalec handle that case automatically instead of
requiring each spec author to know and write the normalized version manually.

Testing

  • go test ./... -run TestGomodReplaceGoModEditArg -v
  • go test --test.short ./...
  • make verify

Copilot AI review requested due to automatic review settings March 31, 2026 09:49
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves Dalec’s gomod replace handling by normalizing replacement targets that specify a v2+ semantic version on a module path that does not use semantic import versioning (/vN)—automatically appending +incompatible to prevent Go module resolution failures.

Changes:

  • Added normalization logic for GomodReplace.goModEditArg() to append +incompatible for v2+ versions when the module path has no major-version suffix.
  • Introduced targeted unit tests covering the new normalization behavior and key non-rewrite cases.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
spec.go Adds normalizeGomodReplaceTarget() and applies it when generating go mod edit -replace arguments.
generator_gomod_test.go Adds/extends tests to validate +incompatible normalization and ensure no rewrites for /vN paths and local replacements.

Automatically normalize gomod replace targets for v2+ module
versions that do not use a /vN module path suffix by appending
+incompatible.

Adds regression coverage for incompatible-version
normalization and ensures existing valid replace forms remain unchanged."

Signed-off-by: Kartik Joshi <karikjoshi21@gmail.com>
@kartikjoshi21 kartikjoshi21 force-pushed the kartikjoshi21/gomod-incompatible-replace branch from ebfc7c5 to b839ee4 Compare March 31, 2026 10:26
@kartikjoshi21 kartikjoshi21 requested a review from Copilot March 31, 2026 10:26
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

expectErr: false,
expectedArg: "github.com/stretchr/testify=github.com/stretchr/testify@v1.8.0",
},
{
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How difficult would be to implement these as integration tests, so we actually pull some stuff for testing?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried moving this into an integration-style test under test/linux_target_test.go, and CI is definitely exercising it now. The issue I hit is that the public +incompatible fixture I used (github.com/docker/distribution) appears to be flaky/problematic across distro CI, so the failure looks fixture-specific rather than a Dalec integration issue. i think i will open up an issue to handle adding these tests under integration.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for looking into it. And did you run IG build with this patch to confirm that generates correct replace?

Copy link
Copy Markdown
Member Author

@kartikjoshi21 kartikjoshi21 Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kartikjoshi@kartikjoshiwindows:~/dalec-project/dalec$ git diff
diff --git a/test/linux_target_test.go b/test/linux_target_test.go
index 87ae1a0..6dc854f 100644
--- a/test/linux_target_test.go
+++ b/test/linux_target_test.go
@@ -2078,6 +2078,77 @@ Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/boot
                })
        })

+       t.Run("temp gomod incompatible replace probe", func(t *testing.T) {
+               t.Parallel()
+               ctx := startTestSpan(baseCtx, t)
+
+               spec := &dalec.Spec{
+                       Name:        "test-gomod-incompatible-probe",
+                       Version:     "0.0.1",
+                       Revision:    "1",
+                       License:     "MIT",
+                       Website:     "https://github.com/project-dalec/dalec",
+                       Vendor:      "Dalec",
+                       Packager:    "Dalec",
+                       Description: "Temporary probe for gomod +incompatible replace",
+                       Sources: map[string]dalec.Source{
+                               "src": {
+                                       Inline: &dalec.SourceInline{
+                                               Dir: &dalec.SourceInlineDir{
+                                                       Files: map[string]*dalec.SourceInlineFile{
+                                                               "go.mod": {Contents: "module example.com/test\n\ngo 1.18\n\nrequire github.com/docker/distribution v2.8.3+incompatible\n"},
+                                                               "main.go": {Contents: `package main
+
+import (
+       "fmt"
+       _ "github.com/docker/distribution/version"
+)
+
+func main() {
+       fmt.Println("hello")
+}
+`},
+                                                       },
+                                               },
+                                       },
+                                       Generate: []*dalec.SourceGenerator{
+                                               {
+                                                       Gomod: &dalec.GeneratorGomod{
+                                                               Edits: &dalec.GomodEdits{
+                                                                       Replace: []dalec.GomodReplace{
+                                                                               {
+                                                                                       Original: "github.com/docker/distribution",
+                                                                                       Update:   "github.com/docker/distribution@v2.8.3",
+                                                                               },
+                                                                       },
+                                                               },
+                                                       },
+                                               },
+                                       },
+                               },
+                       },
+                       Dependencies: &dalec.PackageDependencies{
+                               Build: map[string]dalec.PackageConstraints{
+                                       testConfig.GetPackage("golang"): {},
+                               },
+                       },
+                       Build: dalec.ArtifactBuild{
+                               Steps: []dalec.BuildStep{
+                                       {Command: "grep -F 'replace github.com/docker/distribution => github.com/docker/distribution v2.8.3+incompatible' ./src/go.mod"},
+                                       {Command: "cd ./src && go build"},
+                               },
+                       },
+               }
+
+               testEnv.RunTest(ctx, t, func(ctx context.Context, client gwclient.Client) {
+                       req := newSolveRequest(
+                               withBuildTarget(testConfig.Target.Package),
+                               withSpec(ctx, t, spec),
+                       )
+                       solveT(ctx, t, client, req)
+               })
+       })
+
ild.go:219:
    build.go:219: #86 Build deb package
    build.go:219: #86 DONE 0.1s
    build.go:219:
    build.go:219: #87 Build deb package
    build.go:219: #87 DONE 0.1s
    build.go:219:
    build.go:219: #88 Build deb package
    build.go:219: #88 DONE 0.1s
--- PASS: TestBookworm (0.00s)
    --- PASS: TestBookworm/temp_gomod_incompatible_replace_probe (643.21s)
PASS
ok      github.com/project-dalec/dalec/test     643.390s

@invidian I added this test and things passed locally.

@kartikjoshi21 kartikjoshi21 force-pushed the kartikjoshi21/gomod-incompatible-replace branch 2 times, most recently from a4de639 to b839ee4 Compare April 1, 2026 11:02
@kartikjoshi21 kartikjoshi21 requested a review from invidian April 1, 2026 11:06
Signed-off-by: Kartik Joshi <karikjoshi21@gmail.com>
@cpuguy83
Copy link
Copy Markdown
Collaborator

cpuguy83 commented Apr 7, 2026

@invidian LGTY?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants