mirror of
https://github.com/mvanhorn/cli-printing-press.git
synced 2026-09-14 15:38:08 +08:00
fix(cli): make go install work on Go 1.27 (#4649)
* fix(cli): make go install work on Go 1.27 Pin github.com/enetx/http to v1.0.29 so enetx/http2's go1.27-tagged DisableClientPriority read compiles. Floor the same version in generated go.mod files so regen-merge cannot reintroduce v1.0.28. Co-authored-by: Trevin Chow <tmchow@users.noreply.github.com> * test(cli): cover browser-transport go.mod pin under Go 1.27 Add a browser-chrome-h2 golden/generated-output fixture for the emitted enetx/http pin, and compile the affected generated modules — including the go1.27-tagged http2 source — instead of string-matching go.mod. Co-authored-by: Trevin Chow <tmchow@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Trevin Chow <tmchow@users.noreply.github.com>
This commit is contained in:
@@ -289,7 +289,7 @@ jobs:
|
||||
include:
|
||||
- shard: h3
|
||||
packages: ./internal/generator
|
||||
pattern: ^(TestGenerateBrowserChromeH3Transport)$
|
||||
pattern: ^(TestGenerateBrowserChromeH3Transport|TestGenerateBrowserChromeTransport|TestGenerateCookieHTMLDefaultsBrowserChromeTransport)$
|
||||
- shard: generator
|
||||
packages: ./internal/generator
|
||||
pattern: ^(TestGenerateProjectsCompile|TestGenerateCliutilPackage|TestGenerateFreshnessHelperEmitted|TestGenerateShareEmittedWhenEnabled|TestGenerateAgentContextCommand|TestGenerateHTMLExtractionEndpoint|TestGenerateHTMLExtractionEmbeddedJSONMode|TestGenerateHTMLExtractionPerModeGating|TestGenerateStoreWithBatchResourceDoesNotDuplicateUpsertBatch|TestGenerateStoreUpsertBatchDispatchesToTypedTable|TestGenerateStoreBackfillsIndexedColumnsOnUpgrade|TestGenerateStoreQuotesNumericTableAndDerivedIdentifiers|TestGeneratedOutput_PromotedCommandCompiles|TestGeneratedHelpers_AuthWithKeyURL_Compiles|TestGenerate_CookieAuthUsesBrowserTemplate|TestGenerate_ComposedAuthUsesBrowserTemplate|TestJSONStringParamRejectsInvalidValueBeforeClient|TestGenerateGraphQLBFFUsesSemanticCommandSurface|TestGenerateWhichFallsBackToCommandTree|TestGenerateDependentSyncCompiles|TestGenerateDependentSyncReservedWordCompiles|TestGeneratedSyncTreatsAccessDeniedAsWarning|TestGenerateGraphQLCompiles|TestGenerateMCPCodeOrchestrationEmitsSearchExecute|TestGenerateMCPIntentsEmittedWhenDeclared|TestGenerateMCPEndpointToolsHiddenSuppressesEndpointTools|TestGenerateMCPMainRemoteRuntime|TestGenerateMCPMainRemoteCompiles)$
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ Keep golden artifacts contract-shaped. Snapshot the specific files or output fie
|
||||
|
||||
Maintain `testdata/golden/fixtures/golden-api.yaml` as the purpose-built generated-CLI fixture for the Printing Press. When the machine gains deterministic generation capabilities that should survive major refactors, extend this fixture and add the smallest useful artifact comparison that proves the capability. Do not mutate this fixture for one printed CLI's edge case unless it represents a general machine behavior.
|
||||
|
||||
Device Sniff uses separate deterministic fixtures because device specs are protocol-native rather than OpenAPI-shaped. Use `device-sniff-ble-sample` and `device-sniff-ble-ambiguous` for BLE discovery output. Use `generate-device-ble`, `generate-device-ble-control`, `generate-device-ble-session`, and `generate-device-ble-opaque` for generated device CLI artifacts.
|
||||
Device Sniff uses separate deterministic fixtures because device specs are protocol-native rather than OpenAPI-shaped. Use `device-sniff-ble-sample` and `device-sniff-ble-ambiguous` for BLE discovery output. Use `generate-device-ble`, `generate-device-ble-control`, `generate-device-ble-session`, and `generate-device-ble-opaque` for generated device CLI artifacts. Use `generate-browser-http-transport` for the browser-chrome-h2 `go.mod` emit (surf plus the `enetx/http` pin).
|
||||
|
||||
## Failure handling
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dlclark/regexp2 v1.12.0 // indirect
|
||||
github.com/enetx/g v1.0.224 // indirect
|
||||
github.com/enetx/http v1.0.28 // indirect
|
||||
github.com/enetx/http v1.0.29 // indirect
|
||||
github.com/enetx/http2 v1.0.26 // indirect
|
||||
github.com/enetx/http3 v1.0.7 // indirect
|
||||
github.com/enetx/iter v0.0.0-20250912135656-f1583323588f // indirect
|
||||
|
||||
@@ -19,8 +19,8 @@ github.com/dlclark/regexp2 v1.12.0 h1:0j4c5qQmnC6XOWNjP3PIXURXN2gWx76rd3KvgdPkCz
|
||||
github.com/dlclark/regexp2 v1.12.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/enetx/g v1.0.224 h1:H/uonguFE4qG8YCn5bSpZX5Wh+wTSb+jgf3I2ZM25XM=
|
||||
github.com/enetx/g v1.0.224/go.mod h1:lxhby3LjP8jOTGbxJ/PCd+2Zq1gYiSBbtL/llPhAg5c=
|
||||
github.com/enetx/http v1.0.28 h1:IaNSSDFlAVVdHnYhNIR9wAN7GY4TWL/kkvYC3jOaueY=
|
||||
github.com/enetx/http v1.0.28/go.mod h1:1f4mytfF/SfjATEJnynpwGS6aa1ALjb8DtmYgFVblY0=
|
||||
github.com/enetx/http v1.0.29 h1:B+NVXEN7vTAVYI+kZbx06Jqh/WwhHfUT8nG6OiRpQ78=
|
||||
github.com/enetx/http v1.0.29/go.mod h1:1f4mytfF/SfjATEJnynpwGS6aa1ALjb8DtmYgFVblY0=
|
||||
github.com/enetx/http2 v1.0.26 h1:wy3lYGVwnIUY4Q+gyPPQCJ1a+BMXD1B7Unpyc/Csrxc=
|
||||
github.com/enetx/http2 v1.0.26/go.mod h1:t54ex5HIS8V1+2j6cvEOv6umlrHsbUPFKQ54nYB58Nk=
|
||||
github.com/enetx/http3 v1.0.7 h1:daFhveKBtv8rRallCjaHErzzSHIrq07ovoSvVkvhcMM=
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package generator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
// safeEnetxHTTPVersion is the lowest github.com/enetx/http release whose
|
||||
// http.Server includes DisableClientPriority. enetx/http2 v1.0.25+ compiles a
|
||||
// go1.27-tagged file that reads that field; v1.0.28 and older omit it, so
|
||||
// `go install` on Go 1.27 fails. Keep in sync with the explicit pin in
|
||||
// templates/go.mod.tmpl.
|
||||
const safeEnetxHTTPVersion = "v1.0.29"
|
||||
|
||||
// ensureSafeEnetxHTTP bumps github.com/enetx/http to safeEnetxHTTPVersion when
|
||||
// the generated module resolves it below that version. surf pulls both
|
||||
// enetx/http and enetx/http2; a go.mod.tmpl pin covers the browser-transport
|
||||
// path, but regen-merge can reintroduce a published v1.0.28 require and tidy
|
||||
// can still land on surf's own v1.0.28 floor. Bumping after tidy is exact: it
|
||||
// runs only when enetx/http is in the resolved graph. No-op when the module
|
||||
// is absent or already at/above safeEnetxHTTPVersion.
|
||||
func ensureSafeEnetxHTTP(dir string) error {
|
||||
out, err := runCommand(dir, qualityGateTimeout, "go", "list", "-m", "-f", "{{.Version}}", "github.com/enetx/http")
|
||||
if err != nil {
|
||||
// `go list -m` exits non-zero when enetx/http is not a dependency of
|
||||
// the module — nothing to pin.
|
||||
return nil
|
||||
}
|
||||
// go list -m writes the version to stdout; runCommand joins stdout+stderr,
|
||||
// so take only the first line to ignore any progress/download messages that
|
||||
// the toolchain emits to stderr (e.g. "go: downloading github.com/enetx/http …")
|
||||
// in fresh-cache environments.
|
||||
current := strings.SplitN(strings.TrimSpace(out), "\n", 2)[0]
|
||||
current = strings.TrimSpace(current)
|
||||
if !semver.IsValid(current) || semver.Compare(current, safeEnetxHTTPVersion) >= 0 {
|
||||
return nil
|
||||
}
|
||||
if _, err := runCommand(dir, qualityGateTimeout, "go", "get", "github.com/enetx/http@"+safeEnetxHTTPVersion); err != nil {
|
||||
return fmt.Errorf("bumping github.com/enetx/http to %s: %w", safeEnetxHTTPVersion, err)
|
||||
}
|
||||
if _, err := runCommand(dir, qualityGateTimeout, "go", "mod", "tidy"); err != nil {
|
||||
return fmt.Errorf("re-running go mod tidy after github.com/enetx/http bump: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package generator
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestEnsureSafeEnetxHTTP exercises the post-tidy enetx/http pin against real
|
||||
// modules. It needs network (go get / go mod tidy), so it skips in -short.
|
||||
func TestEnsureSafeEnetxHTTP(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("ensureSafeEnetxHTTP runs go get / go mod tidy (network)")
|
||||
}
|
||||
|
||||
t.Run("bumps a transitively-old enetx/http to the safe version", func(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
require.NoError(t, os.WriteFile(filepath.Join(dir, "go.mod"),
|
||||
[]byte("module enetxhttpbump\n\ngo 1.24.0\n\nrequire github.com/enetx/http v1.0.28\n"), 0o644))
|
||||
require.NoError(t, os.WriteFile(filepath.Join(dir, "main.go"),
|
||||
[]byte("package main\n\nimport _ \"github.com/enetx/http\"\n\nfunc main() {}\n"), 0o644))
|
||||
if _, err := runCommand(dir, qualityGateTimeout, "go", "mod", "tidy"); err != nil {
|
||||
t.Skipf("initial go mod tidy failed (offline?): %v", err)
|
||||
}
|
||||
|
||||
require.NoError(t, ensureSafeEnetxHTTP(dir))
|
||||
|
||||
out, err := runCommand(dir, qualityGateTimeout, "go", "list", "-m", "-f", "{{.Version}}", "github.com/enetx/http")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, safeEnetxHTTPVersion, out, "enetx/http should be bumped to the safe version")
|
||||
|
||||
before, err := os.ReadFile(filepath.Join(dir, "go.mod"))
|
||||
require.NoError(t, err)
|
||||
_, err = runCommand(dir, qualityGateTimeout, "go", "mod", "tidy")
|
||||
require.NoError(t, err)
|
||||
after, err := os.ReadFile(filepath.Join(dir, "go.mod"))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, string(before), string(after), "go.mod must be tidy after the bump")
|
||||
})
|
||||
|
||||
t.Run("no-op when enetx/http is not a dependency", func(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
require.NoError(t, os.WriteFile(filepath.Join(dir, "go.mod"),
|
||||
[]byte("module noenetxhttp\n\ngo 1.24.0\n"), 0o644))
|
||||
require.NoError(t, os.WriteFile(filepath.Join(dir, "main.go"),
|
||||
[]byte("package main\n\nfunc main() {}\n"), 0o644))
|
||||
|
||||
require.NoError(t, ensureSafeEnetxHTTP(dir))
|
||||
|
||||
gomod, err := os.ReadFile(filepath.Join(dir, "go.mod"))
|
||||
require.NoError(t, err)
|
||||
assert.NotContains(t, string(gomod), "github.com/enetx/http",
|
||||
"absent enetx/http must not gain an unused require")
|
||||
})
|
||||
}
|
||||
|
||||
func TestSafeEnetxHTTPVersionMatchesGoModTemplate(t *testing.T) {
|
||||
tmpl, err := os.ReadFile(filepath.Join("templates", "go.mod.tmpl"))
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, string(tmpl), "github.com/enetx/http "+safeEnetxHTTPVersion,
|
||||
"templates/go.mod.tmpl pin must stay in sync with safeEnetxHTTPVersion")
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
||||
@@ -2585,18 +2586,66 @@ func countFiles(t *testing.T, root string) int {
|
||||
}
|
||||
|
||||
func runGoCommand(t *testing.T, dir string, args ...string) {
|
||||
t.Helper()
|
||||
runGoCommandWithEnv(t, dir, nil, args...)
|
||||
}
|
||||
|
||||
func runGoCommandWithEnv(t *testing.T, dir string, extraEnv []string, args ...string) {
|
||||
t.Helper()
|
||||
if testing.Short() && len(args) > 0 && (args[0] == "build" || args[0] == "test") {
|
||||
t.Skip("generated CLI compile tests run in the full generated-test CI lane")
|
||||
}
|
||||
runGoCommandRequired(t, dir, args...)
|
||||
output, err := runGoCommandOutputWithEnv(t, dir, extraEnv, args...)
|
||||
require.NoError(t, err, output)
|
||||
}
|
||||
|
||||
func requireGeneratedCompiles(t *testing.T, dir string) {
|
||||
t.Helper()
|
||||
requireGeneratedCompilesWithEnv(t, dir, nil)
|
||||
}
|
||||
|
||||
func requireGeneratedCompilesWithEnv(t *testing.T, dir string, extraEnv []string) {
|
||||
t.Helper()
|
||||
// No-op in this test harness; module resolution is exercised via -mod=mod.
|
||||
runGoCommand(t, dir, "mod", "tidy")
|
||||
runGoCommand(t, dir, "build", "./...")
|
||||
runGoCommandWithEnv(t, dir, extraEnv, "mod", "tidy")
|
||||
runGoCommandWithEnv(t, dir, extraEnv, "build", "./...")
|
||||
}
|
||||
|
||||
const go127Toolchain = "go1.27.0"
|
||||
|
||||
var (
|
||||
go127Once sync.Once
|
||||
go127Avail bool
|
||||
go127Reason string
|
||||
)
|
||||
|
||||
func go127ToolchainAvailable(t *testing.T) bool {
|
||||
t.Helper()
|
||||
go127Once.Do(func() {
|
||||
cmd := exec.Command("go", "version")
|
||||
cmd.Env = append(os.Environ(), "GOTOOLCHAIN="+go127Toolchain)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
go127Reason = strings.TrimSpace(string(out) + " " + err.Error())
|
||||
return
|
||||
}
|
||||
go127Avail = strings.Contains(string(out), "go1.27")
|
||||
if !go127Avail {
|
||||
go127Reason = strings.TrimSpace(string(out))
|
||||
}
|
||||
})
|
||||
if !go127Avail && go127Reason != "" {
|
||||
t.Logf("Go 1.27 toolchain unavailable: %s", go127Reason)
|
||||
}
|
||||
return go127Avail
|
||||
}
|
||||
|
||||
func requireGeneratedCompilesGo127(t *testing.T, dir string) {
|
||||
t.Helper()
|
||||
if !go127ToolchainAvailable(t) {
|
||||
t.Skip("Go 1.27 toolchain not available to typecheck enetx/http2's go1.27-tagged source")
|
||||
}
|
||||
requireGeneratedCompilesWithEnv(t, dir, []string{"GOTOOLCHAIN=" + go127Toolchain})
|
||||
}
|
||||
|
||||
func runGoCommandRequired(t *testing.T, dir string, args ...string) {
|
||||
@@ -2611,6 +2660,11 @@ func runGoCommandRequired(t *testing.T, dir string, args ...string) {
|
||||
// the usual must-succeed case.
|
||||
func runGoCommandOutput(t *testing.T, dir string, args ...string) (string, error) {
|
||||
t.Helper()
|
||||
return runGoCommandOutputWithEnv(t, dir, nil, args...)
|
||||
}
|
||||
|
||||
func runGoCommandOutputWithEnv(t *testing.T, dir string, extraEnv []string, args ...string) (string, error) {
|
||||
t.Helper()
|
||||
|
||||
// Generated-project compile tests exercise module resolution via -mod=mod;
|
||||
// production Validate still owns the go mod tidy quality gate.
|
||||
@@ -2627,6 +2681,7 @@ func runGoCommandOutput(t *testing.T, dir string, args ...string) (string, error
|
||||
require.NoError(t, err)
|
||||
cmd.Env = append(os.Environ(), "GOCACHE="+cacheDir)
|
||||
cmd.Env = append(cmd.Env, sandboxHomeEnv(t)...)
|
||||
cmd.Env = append(cmd.Env, extraEnv...)
|
||||
output, err := cmd.CombinedOutput()
|
||||
return string(output), err
|
||||
}
|
||||
@@ -2850,6 +2905,7 @@ func TestGenerateBrowserChromeTransport(t *testing.T) {
|
||||
assert.Contains(t, string(gomod), "go "+currentGoDirectiveVersion()+"\n")
|
||||
assert.Contains(t, string(gomod), "toolchain "+currentGoToolchainVersion())
|
||||
assert.Contains(t, string(gomod), "github.com/enetx/surf")
|
||||
assert.Contains(t, string(gomod), "github.com/enetx/http "+safeEnetxHTTPVersion)
|
||||
|
||||
clientGo, err := os.ReadFile(filepath.Join(outputDir, "internal", "client", "client.go"))
|
||||
require.NoError(t, err)
|
||||
@@ -2862,8 +2918,9 @@ func TestGenerateBrowserChromeTransport(t *testing.T) {
|
||||
readme, err := os.ReadFile(filepath.Join(outputDir, "README.md"))
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, string(readme), "Chrome-compatible HTTP transport")
|
||||
// The H3 sibling below compiles the same surf-backed client path; this
|
||||
// test stays focused on the non-H3 rendering branch.
|
||||
|
||||
requireGeneratedCompiles(t, outputDir)
|
||||
requireGeneratedCompilesGo127(t, outputDir)
|
||||
}
|
||||
|
||||
func TestGenerateBrowserChromeH3Transport(t *testing.T) {
|
||||
@@ -3280,12 +3337,15 @@ func TestGenerateCookieHTMLDefaultsBrowserChromeTransport(t *testing.T) {
|
||||
|
||||
gomod := readGeneratedFile(t, outputDir, "go.mod")
|
||||
assert.Contains(t, gomod, "github.com/enetx/surf")
|
||||
assert.Contains(t, gomod, "github.com/enetx/http "+safeEnetxHTTPVersion)
|
||||
|
||||
clientGo := readGeneratedFile(t, outputDir, "internal", "client", "client.go")
|
||||
assert.Contains(t, clientGo, `"github.com/enetx/surf"`)
|
||||
assert.Contains(t, clientGo, "Impersonate()")
|
||||
assert.Contains(t, clientGo, "Chrome()")
|
||||
assert.NotContains(t, clientGo, `req.Header.Set("User-Agent", "cookiehtml-pp-cli/0.1.0")`)
|
||||
|
||||
requireGeneratedCompiles(t, outputDir)
|
||||
}
|
||||
|
||||
func TestGenerateHTMLExtractionEndpoint(t *testing.T) {
|
||||
|
||||
@@ -269,15 +269,18 @@ func GenerateFromPlan(planSpec *PlanSpec, outputDir string) error {
|
||||
return fmt.Errorf("running go mod tidy: %w", err)
|
||||
}
|
||||
|
||||
// Pin golang.org/x/net and golang.org/x/text to patched releases when they
|
||||
// resolved transitively below the safe versions (see ensureSafeXNet and
|
||||
// ensureSafeXText).
|
||||
// Pin golang.org/x/net, golang.org/x/text, and github.com/enetx/http to
|
||||
// safe releases when they resolved transitively below the floors (see
|
||||
// ensureSafeXNet, ensureSafeXText, and ensureSafeEnetxHTTP).
|
||||
if err := ensureSafeXNet(outputDir); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ensureSafeXText(outputDir); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ensureSafeEnetxHTTP(outputDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -65,4 +65,10 @@ require golang.org/x/sys v0.46.0
|
||||
// is present, so MVS keeps the floor; tidy drops it for CLIs without surf.
|
||||
require golang.org/x/crypto v0.53.0 // indirect
|
||||
require github.com/quic-go/quic-go v0.60.0 // indirect
|
||||
// enetx/http2 v1.0.25+ reads http.Server.DisableClientPriority under
|
||||
// //go:build go1.27. Surf still floors http at v1.0.28, which lacks that
|
||||
// field; pin v1.0.29 so printed CLIs `go install` on Go 1.27. Keep in
|
||||
// sync with safeEnetxHTTPVersion. regen-merge: fresh wins on this path,
|
||||
// so a published v1.0.28 cannot re-enter.
|
||||
require github.com/enetx/http v1.0.29 // indirect
|
||||
{{- end}}
|
||||
|
||||
@@ -61,6 +61,12 @@ func (g *Generator) Validate() error {
|
||||
return ensureSafeXText(g.OutputDir)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ensure safe github.com/enetx/http",
|
||||
run: func() error {
|
||||
return ensureSafeEnetxHTTP(g.OutputDir)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "go test ./...",
|
||||
run: func() error {
|
||||
|
||||
@@ -232,3 +232,54 @@ require github.com/spf13/cobra v1.9.1
|
||||
assert.Equal(t, "v1.9.1", gotPaths["github.com/spf13/cobra"],
|
||||
"shared deps stay at fresh's version")
|
||||
}
|
||||
|
||||
// TestRenderMergedGoModFreshWinsEnetxHTTP pins that regen-merge cannot
|
||||
// reintroduce github.com/enetx/http v1.0.28 (the Go 1.27 http2 compile
|
||||
// break) when the fresh tree floors v1.0.29. Fresh already wins on shared
|
||||
// require paths; this case locks that rule to the pin that makes `go install`
|
||||
// work on Go 1.27.
|
||||
func TestRenderMergedGoModFreshWinsEnetxHTTP(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tmp := t.TempDir()
|
||||
pubDir := filepath.Join(tmp, "pub")
|
||||
freshDir := filepath.Join(tmp, "fresh")
|
||||
require.NoError(t, os.MkdirAll(pubDir, 0o755))
|
||||
require.NoError(t, os.MkdirAll(freshDir, 0o755))
|
||||
|
||||
pubGoMod := []byte(`module github.com/example/monorepo/library/foo
|
||||
|
||||
go 1.26.6
|
||||
|
||||
require (
|
||||
github.com/enetx/http v1.0.28
|
||||
github.com/enetx/http2 v1.0.26
|
||||
github.com/enetx/surf v1.0.199
|
||||
)
|
||||
`)
|
||||
freshGoMod := []byte(`module foo-pp-cli
|
||||
|
||||
go 1.26.6
|
||||
|
||||
require (
|
||||
github.com/enetx/http v1.0.29
|
||||
github.com/enetx/http2 v1.0.26
|
||||
github.com/enetx/surf v1.0.199
|
||||
)
|
||||
`)
|
||||
require.NoError(t, writeFileAtomic(filepath.Join(pubDir, "go.mod"), pubGoMod))
|
||||
require.NoError(t, writeFileAtomic(filepath.Join(freshDir, "go.mod"), freshGoMod))
|
||||
|
||||
bytes, err := renderMergedGoMod(pubDir, freshDir)
|
||||
require.NoError(t, err)
|
||||
parsed, err := modfile.Parse("merged-go.mod", bytes, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
gotPaths := map[string]string{}
|
||||
for _, req := range parsed.Require {
|
||||
gotPaths[req.Mod.Path] = req.Mod.Version
|
||||
}
|
||||
assert.Equal(t, "v1.0.29", gotPaths["github.com/enetx/http"],
|
||||
"fresh's Go 1.27-compatible enetx/http pin must win over published v1.0.28")
|
||||
assert.Equal(t, "v1.0.26", gotPaths["github.com/enetx/http2"])
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ source-level tests and byte-level goldens can miss.
|
||||
|
||||
When no case is supplied, a focused default set covers endpoint templates,
|
||||
MCP/code-orchestration, rich auth, GraphQL shared endpoints, learn-loop
|
||||
emission, and generated BLE device variants. Pass extra case names when a fix
|
||||
touches another generated variant.
|
||||
emission, generated BLE device variants, and browser-transport go.mod.
|
||||
Pass extra case names when a fix touches another generated variant.
|
||||
USAGE
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ default_cases=(
|
||||
generate-device-ble-control
|
||||
generate-device-ble-session
|
||||
generate-device-ble-opaque
|
||||
generate-browser-http-transport
|
||||
)
|
||||
|
||||
cases=()
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# Locks the browser-transport go.mod contract: surf plus the enetx/http
|
||||
# pin (and the x/crypto / quic-go floors). Standard-HTTP generate cases
|
||||
# never emit this block.
|
||||
|
||||
browser-http-transport/go.mod
|
||||
@@ -0,0 +1,2 @@
|
||||
set -euo pipefail
|
||||
"$BINARY" generate --spec testdata/golden/fixtures/browser-http-transport.yaml --output "$CASE_ACTUAL_DIR/browser-http-transport" --force --spec-url file://testdata/golden/fixtures/browser-http-transport.yaml --validate=false
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
module browser-http-transport-pp-cli
|
||||
|
||||
go 1.26.6
|
||||
|
||||
toolchain go1.26.6
|
||||
|
||||
require (
|
||||
github.com/enetx/surf v1.0.199
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/spf13/pflag v1.0.6
|
||||
github.com/pelletier/go-toml/v2 v2.2.4
|
||||
)
|
||||
require modernc.org/sqlite v1.37.0
|
||||
require github.com/mark3labs/mcp-go v0.57.0
|
||||
|
||||
// x/sys is a DIRECT dependency even without auth: filelock_windows.go
|
||||
// imports golang.org/x/sys/windows. Emitted as a direct require (no
|
||||
// // indirect) so a Windows cross-compile of a freshly generated bundle
|
||||
// succeeds without a manual `go mod tidy`. The version matches the
|
||||
// transitive floor. NOTE (go mod tidy GOOS caveat): the import is behind
|
||||
// `//go:build windows`, so tidy under GOOS=linux/darwin re-marks this
|
||||
// // indirect; under GOOS=windows it stays direct.
|
||||
require golang.org/x/sys v0.46.0
|
||||
|
||||
// Floor the HTTP/3 transitive deps pulled in only via github.com/enetx/surf
|
||||
// above their vulnerable versions (osv flags module presence; govulncheck
|
||||
// reachability = 0 for these REST CLIs). Emitted only when the surf transport
|
||||
// is present, so MVS keeps the floor; tidy drops it for CLIs without surf.
|
||||
require golang.org/x/crypto v0.53.0 // indirect
|
||||
require github.com/quic-go/quic-go v0.60.0 // indirect
|
||||
// enetx/http2 v1.0.25+ reads http.Server.DisableClientPriority under
|
||||
// //go:build go1.27. Surf still floors http at v1.0.28, which lacks that
|
||||
// field; pin v1.0.29 so printed CLIs `go install` on Go 1.27. Keep in
|
||||
// sync with safeEnetxHTTPVersion. regen-merge: fresh wins on this path,
|
||||
// so a published v1.0.28 cannot re-enter.
|
||||
require github.com/enetx/http v1.0.29 // indirect
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1,3 @@
|
||||
warning: could not derive run_id from --research-dir; phase5 dogfood acceptance will refuse to write without it
|
||||
agentcookie: skipping browser-http-transport (cookie-only or no env-var auth)
|
||||
Generated browser-http-transport at <ARTIFACT_DIR>/generate-browser-http-transport/browser-http-transport
|
||||
@@ -0,0 +1,21 @@
|
||||
name: browser-http-transport
|
||||
description: |
|
||||
Golden fixture for the browser-chrome-h2 go.mod emit: surf plus the
|
||||
enetx/http pin required so printed CLIs `go install` on Go 1.27.
|
||||
Existing generate-* cases use standard HTTP and never snapshot this
|
||||
require block.
|
||||
version: 1.0.0
|
||||
base_url: https://www.example.com
|
||||
http_transport: browser-chrome-h2
|
||||
auth:
|
||||
type: none
|
||||
resources:
|
||||
posts:
|
||||
description: Browse posts
|
||||
endpoints:
|
||||
list:
|
||||
method: GET
|
||||
path: /
|
||||
description: List posts
|
||||
learn:
|
||||
disabled: true
|
||||
Reference in New Issue
Block a user