From 7eebbbbfc3056ebc4f593e0eb0a3387b796a74b7 Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Sat, 10 Oct 2026 14:12:50 +0000 Subject: [PATCH] Accept valid local main module paths --- cmd/releasekit/local_module_test.go | 44 +++++++++++++++++++++++ docs/roadmap/remediation.md | 9 ++++- internal/releaseidentity/identity.go | 2 +- internal/releaseidentity/identity_test.go | 4 +++ 4 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 cmd/releasekit/local_module_test.go diff --git a/cmd/releasekit/local_module_test.go b/cmd/releasekit/local_module_test.go new file mode 100644 index 0000000..3f08759 --- /dev/null +++ b/cmd/releasekit/local_module_test.go @@ -0,0 +1,44 @@ +package main + +import ( + "bytes" + "context" + "path/filepath" + "strings" + "testing" + + "gitea.maximumdirect.net/eric/ReleaseKit/cmd/releasekit/internal/cli" +) + +func TestMainModuleWithoutDomainSupportsCheckBuildAndPageDelivery(t *testing.T) { + repository := newDeliveryBuildRepository(t) + writeRepositoryFile(t, repository, "go.mod", "module example\n\ngo 1.26.0\n") + cliGitRun(t, repository, "add", ".") + cliGitRun(t, repository, "commit", "-m", "Use local main module path") + state := &deliveryServerState{} + server := newDeliveryServer(t, state) + defer server.Close() + settings := cli.Settings{Product: "Example Product", + Build: &cli.BuildSettings{MainPackage: ".", BinaryName: "helper", Targets: []string{"linux/amd64"}}, + Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"}, + } + writeDeliveryProfile(t, repository, settings) + for _, args := range [][]string{ + {"check", "--repo", repository, "v1.2.3"}, + {"build", "--repo", repository, "--output", filepath.Join(t.TempDir(), "assets"), "v1.2.3"}, + } { + var stdout, stderr bytes.Buffer + if status := run(context.Background(), args, &stdout, &stderr); status != 0 || !strings.Contains(stdout.String(), "module example version v1.2.3 tag v1.2.3") { + t.Fatalf("%s status=%d stdout=%s stderr=%s", args[0], status, stdout.String(), stderr.String()) + } + } + settings.Build = nil + writeDeliveryProfile(t, repository, settings) + state.commit = cliGitOutput(t, repository, "rev-parse", "HEAD") + cliGitRun(t, repository, "tag", "v1.2.3") + t.Setenv("RELEASEKIT_GITEA_TOKEN", "fixture-token") + var stdout, stderr bytes.Buffer + if status := run(context.Background(), []string{"deliver", "--repo", repository, "v1.2.3"}, &stdout, &stderr); status != 0 || state.releaseCreates != 1 || state.assetUploads != 0 { + t.Fatalf("page delivery status=%d creates=%d uploads=%d stderr=%s", status, state.releaseCreates, state.assetUploads, stderr.String()) + } +} diff --git a/docs/roadmap/remediation.md b/docs/roadmap/remediation.md index 925e931..1201259 100644 --- a/docs/roadmap/remediation.md +++ b/docs/roadmap/remediation.md @@ -195,7 +195,14 @@ and cancellation tests. ## Issue 6: Avoid requiring downloadable module paths for ordinary main modules (P2) -Status: open. Category: unrequested restriction. +Status: complete. Category: unrequested restriction. + +**Completion:** Identity derivation uses Go import-path lexical validation and +retains path-major compatibility. A valid non-domain main module now completes +candidate checking, native standalone compilation, and hosted page-only delivery +against a local fixture. Qualified and local major-version derivation, missing +and malformed declarations, and incompatible majors pass their focused offline +regression tests. **Evidence:** [`Derive`](../../internal/releaseidentity/identity.go) adds `module.CheckPath`, including its requirement for a dot in the first path element. diff --git a/internal/releaseidentity/identity.go b/internal/releaseidentity/identity.go index a512ece..2c8cfba 100644 --- a/internal/releaseidentity/identity.go +++ b/internal/releaseidentity/identity.go @@ -126,7 +126,7 @@ func Derive(selection Selection, declaration []byte, version string) (Identity, return identity, fmt.Errorf("selected module declaration has no valid module directive") } identity.ModulePath = modulePath - if err := module.CheckPath(modulePath); err != nil { + if err := module.CheckImportPath(modulePath); err != nil { return identity, fmt.Errorf("invalid module path %q: %w", modulePath, err) } _, pathMajor, ok := module.SplitPathVersion(modulePath) diff --git a/internal/releaseidentity/identity_test.go b/internal/releaseidentity/identity_test.go index 1855e49..ed956d4 100644 --- a/internal/releaseidentity/identity_test.go +++ b/internal/releaseidentity/identity_test.go @@ -19,6 +19,8 @@ func TestDeriveUsesModuleDeclarationAndPhysicalLayout(t *testing.T) { wantNote string }{ {name: "root v1", subdir: ".", modulePath: "example.com/root", version: "v1.2.3", wantTag: "v1.2.3", wantNote: "docs/releases/v1.2.3.md"}, + {name: "local main module", subdir: ".", modulePath: "example", version: "v1.2.3", wantTag: "v1.2.3", wantNote: "docs/releases/v1.2.3.md"}, + {name: "local major module", subdir: "apps/local/v2", modulePath: "example/v2", version: "v2.3.4", wantTag: "apps/local/v2.3.4", wantNote: "apps/local/v2/docs/releases/v2.3.4.md"}, {name: "nested", subdir: "integrations/check", modulePath: "example.com/check", version: "v1.2.3", wantTag: "integrations/check/v1.2.3", wantNote: "integrations/check/docs/releases/v1.2.3.md"}, {name: "root major path", subdir: ".", modulePath: "example.com/root/v2", version: "v2.3.4", wantTag: "v2.3.4", wantNote: "docs/releases/v2.3.4.md"}, {name: "root physical major directory", subdir: "v2", modulePath: "example.com/root/v2", version: "v2.3.4", wantTag: "v2.3.4", wantNote: "v2/docs/releases/v2.3.4.md"}, @@ -52,6 +54,8 @@ func TestDeriveRejectsInvalidModuleAndVersionIdentity(t *testing.T) { {name: "missing module directive", declaration: "go 1.26\n", version: "v1.2.3"}, {name: "malformed module path", declaration: "module example.com/../bad\n", version: "v1.2.3"}, {name: "incompatible major", declaration: "module example.com/service/v2\n", version: "v1.2.3"}, + {name: "incompatible local major", declaration: "module example/v2\n", version: "v1.2.3"}, + {name: "invalid local module path", declaration: "module example/../bad\n", version: "v1.2.3"}, {name: "unstable version", declaration: "module example.com/service\n", version: "v1.2.3-rc.1"}, } { t.Run(tt.name, func(t *testing.T) {