From 23540772c94b4883f6b2a035f00bd4de9297d0f3 Mon Sep 17 00:00:00 2001 From: RW Date: Sun, 13 Jul 2025 13:56:08 +0200 Subject: [PATCH 1/6] Run go mod maintenance after migration --- cmd/go_mod.go | 61 +++++++++++++++++++++++++++++++++++++++++++++ cmd/migrate.go | 4 +++ cmd/migrate_test.go | 52 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+) create mode 100644 cmd/go_mod.go create mode 100644 cmd/migrate_test.go diff --git a/cmd/go_mod.go b/cmd/go_mod.go new file mode 100644 index 0000000..9422a8f --- /dev/null +++ b/cmd/go_mod.go @@ -0,0 +1,61 @@ +package cmd + +import ( + "bytes" + "io/fs" + "os" + "path/filepath" +) + +// runGoMod executes `go mod tidy`, `go mod download` and `go mod vendor` +// inside every directory under root that contains a go.mod file referencing +// github.com/gofiber/fiber. Directories named `vendor` are skipped. +func runGoMod(root string) error { + dirs, err := fiberModuleDirs(root) + if err != nil { + return err + } + for _, dir := range dirs { + commands := [][]string{ + {"go", "mod", "tidy"}, + {"go", "mod", "download"}, + {"go", "mod", "vendor"}, + } + for _, args := range commands { + cmd := execCommand(args[0], args[1:]...) + cmd.Dir = dir + if err := runCmd(cmd); err != nil { + return err + } + } + } + return nil +} + +// fiberModuleDirs returns directories under root containing a go.mod file that +// requires github.com/gofiber/fiber. vendor directories are skipped. +func fiberModuleDirs(root string) ([]string, error) { + var dirs []string + err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if d.IsDir() && d.Name() == "vendor" { + return filepath.SkipDir + } + if !d.IsDir() && d.Name() == "go.mod" { + b, err := os.ReadFile(path) + if err != nil { + return err + } + if bytes.Contains(b, []byte("github.com/gofiber/fiber")) { + dirs = append(dirs, filepath.Dir(path)) + } + } + return nil + }) + if err != nil { + return nil, err + } + return dirs, nil +} diff --git a/cmd/migrate.go b/cmd/migrate.go index a9508a9..d0acab2 100644 --- a/cmd/migrate.go +++ b/cmd/migrate.go @@ -59,6 +59,10 @@ func migrateRunE(cmd *cobra.Command, _ []string) error { return fmt.Errorf("migration failed %w", err) } + if err := runGoMod(wd); err != nil { + return fmt.Errorf("go mod: %w", err) + } + msg := fmt.Sprintf("Migration from Fiber %s to %s", currentVersionS, targetVersionS) cmd.Println(termenv.String(msg). Foreground(termenv.ANSIBrightBlue)) diff --git a/cmd/migrate_test.go b/cmd/migrate_test.go new file mode 100644 index 0000000..a014f97 --- /dev/null +++ b/cmd/migrate_test.go @@ -0,0 +1,52 @@ +package cmd + +import ( + "os" + "os/exec" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_RunGoMod(t *testing.T) { + dir := t.TempDir() + + modContent := `module example + +require github.com/gofiber/fiber/v2 v2.0.0` + require.NoError(t, os.WriteFile(filepath.Join(dir, "go.mod"), []byte(modContent), 0o600)) + + vendor := filepath.Join(dir, "vendor") + require.NoError(t, os.Mkdir(vendor, 0o755)) + require.NoError(t, os.WriteFile(filepath.Join(vendor, "go.mod"), []byte("module vendor"), 0o600)) + + origExec := execCommand + var cmds []*exec.Cmd + execCommand = func(name string, args ...string) *exec.Cmd { + cs := append([]string{"-test.run=TestHelperProcess", "--", name}, args...) + cmd := exec.Command(os.Args[0], cs...) + env := []string{"GO_WANT_HELPER_PROCESS=1"} + if needError { + env = append(env, "GO_WANT_HELPER_NEED_ERR=1") + } + cmd.Env = env + cmds = append(cmds, cmd) + return cmd + } + defer func() { + execCommand = origExec + needError = false + }() + + require.NoError(t, runGoMod(dir)) + assert.Len(t, cmds, 3) + for _, c := range cmds { + assert.Equal(t, dir, c.Dir) + } + + cmds = nil + needError = true + assert.Error(t, runGoMod(dir)) +} From 2f5aafffd518c14783e59e6235fd58a872b0673f Mon Sep 17 00:00:00 2001 From: RW Date: Sun, 13 Jul 2025 14:08:37 +0200 Subject: [PATCH 2/6] Add Go version migration --- cmd/internal/migrations/lists.go | 1 + cmd/internal/migrations/v3/common_test.go | 31 +++++++++ cmd/internal/migrations/v3/go_version.go | 83 +++++++++++++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 cmd/internal/migrations/v3/go_version.go diff --git a/cmd/internal/migrations/lists.go b/cmd/internal/migrations/lists.go index d6a4a34..3cc412e 100644 --- a/cmd/internal/migrations/lists.go +++ b/cmd/internal/migrations/lists.go @@ -56,6 +56,7 @@ var Migrations = []Migration{ v3migrations.MigrateEnvVarConfig, v3migrations.MigrateSessionConfig, v3migrations.MigrateReqHeaderParser, + v3migrations.MigrateGoVersion("1.22"), }, }, } diff --git a/cmd/internal/migrations/v3/common_test.go b/cmd/internal/migrations/v3/common_test.go index 55c42ba..858055a 100644 --- a/cmd/internal/migrations/v3/common_test.go +++ b/cmd/internal/migrations/v3/common_test.go @@ -775,3 +775,34 @@ var _ = session.New(session.Config{ assert.NotContains(t, content, "Expiration:") assert.Contains(t, buf.String(), "Migrating session middleware configs") } + +func Test_MigrateGoVersion(t *testing.T) { + t.Parallel() + + dir, err := os.MkdirTemp("", "mgover") + require.NoError(t, err) + defer func() { require.NoError(t, os.RemoveAll(dir)) }() + + mod := `module example + +go 1.21 + +require github.com/gofiber/fiber/v2 v2.0.0` + require.NoError(t, os.WriteFile(filepath.Join(dir, "go.mod"), []byte(mod), 0o600)) + + vendor := filepath.Join(dir, "vendor") + require.NoError(t, os.Mkdir(vendor, 0o755)) + require.NoError(t, os.WriteFile(filepath.Join(vendor, "go.mod"), []byte("module vendor\n\ngo 1.10"), 0o600)) + + var buf bytes.Buffer + cmd := newCmd(&buf) + fn := MigrateGoVersion("1.23") + require.NoError(t, fn(cmd, dir, nil, nil)) + + content := readFile(t, filepath.Join(dir, "go.mod")) + assert.Contains(t, content, "go 1.23") + assert.Contains(t, buf.String(), "1.23") + + vendorContent := readFile(t, filepath.Join(vendor, "go.mod")) + assert.Contains(t, vendorContent, "go 1.10") +} diff --git a/cmd/internal/migrations/v3/go_version.go b/cmd/internal/migrations/v3/go_version.go new file mode 100644 index 0000000..f6e2937 --- /dev/null +++ b/cmd/internal/migrations/v3/go_version.go @@ -0,0 +1,83 @@ +package v3 + +import ( + "bytes" + "fmt" + "io/fs" + "os" + "path/filepath" + "strings" + + semver "github.com/Masterminds/semver/v3" + "github.com/spf13/cobra" +) + +// MigrateGoVersion ensures that all go.mod files referencing Fiber +// declare at least the provided Go version. Vendor directories are skipped. +func MigrateGoVersion(minVersion string) func(*cobra.Command, string, *semver.Version, *semver.Version) error { + minVer := semver.MustParse(minVersion) + return func(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { + dirs, err := fiberModuleDirs(cwd) + if err != nil { + return err + } + for _, dir := range dirs { + modFile := filepath.Join(dir, "go.mod") + b, err := os.ReadFile(modFile) + if err != nil { + return fmt.Errorf("read %s: %w", modFile, err) + } + lines := strings.Split(string(b), "\n") + changed := false + for i, line := range lines { + line = strings.TrimSpace(line) + if strings.HasPrefix(line, "go ") { + currVer, err := semver.NewVersion(strings.TrimSpace(strings.TrimPrefix(line, "go"))) + if err != nil { + return fmt.Errorf("parse go version in %s: %w", modFile, err) + } + if currVer.LessThan(minVer) { + lines[i] = "go " + minVer.String() + changed = true + } + break + } + } + if changed { + if err := os.WriteFile(modFile, []byte(strings.Join(lines, "\n")), 0o600); err != nil { + return fmt.Errorf("write %s: %w", modFile, err) + } + } + } + cmd.Printf("Ensuring go version >= %s\n", minVer.String()) + return nil + } +} + +// fiberModuleDirs returns directories under root containing a go.mod file that +// requires github.com/gofiber/fiber. vendor directories are skipped. +func fiberModuleDirs(root string) ([]string, error) { + var dirs []string + err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if d.IsDir() && d.Name() == "vendor" { + return filepath.SkipDir + } + if !d.IsDir() && d.Name() == "go.mod" { + b, err := os.ReadFile(path) + if err != nil { + return err + } + if bytes.Contains(b, []byte("github.com/gofiber/fiber")) { + dirs = append(dirs, filepath.Dir(path)) + } + } + return nil + }) + if err != nil { + return nil, err + } + return dirs, nil +} From 75786cde231265fcf5fded46c5269b8dcafb80ba Mon Sep 17 00:00:00 2001 From: RW Date: Sun, 13 Jul 2025 14:54:19 +0200 Subject: [PATCH 3/6] Move go version migration to general migrations --- .../migrations/{v3 => }/go_version.go | 6 +- cmd/internal/migrations/lists.go | 2 +- cmd/internal/migrations/v3/common_test.go | 62 ++++++++++--------- 3 files changed, 36 insertions(+), 34 deletions(-) rename cmd/internal/migrations/{v3 => }/go_version.go (95%) diff --git a/cmd/internal/migrations/v3/go_version.go b/cmd/internal/migrations/go_version.go similarity index 95% rename from cmd/internal/migrations/v3/go_version.go rename to cmd/internal/migrations/go_version.go index f6e2937..d931d21 100644 --- a/cmd/internal/migrations/v3/go_version.go +++ b/cmd/internal/migrations/go_version.go @@ -1,4 +1,4 @@ -package v3 +package migrations import ( "bytes" @@ -12,8 +12,8 @@ import ( "github.com/spf13/cobra" ) -// MigrateGoVersion ensures that all go.mod files referencing Fiber -// declare at least the provided Go version. Vendor directories are skipped. +// MigrateGoVersion ensures that all go.mod files referencing Fiber declare at +// least the provided Go version. Vendor directories are skipped. func MigrateGoVersion(minVersion string) func(*cobra.Command, string, *semver.Version, *semver.Version) error { minVer := semver.MustParse(minVersion) return func(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { diff --git a/cmd/internal/migrations/lists.go b/cmd/internal/migrations/lists.go index 3cc412e..ae7e1cf 100644 --- a/cmd/internal/migrations/lists.go +++ b/cmd/internal/migrations/lists.go @@ -56,7 +56,7 @@ var Migrations = []Migration{ v3migrations.MigrateEnvVarConfig, v3migrations.MigrateSessionConfig, v3migrations.MigrateReqHeaderParser, - v3migrations.MigrateGoVersion("1.22"), + MigrateGoVersion("1.24"), }, }, } diff --git a/cmd/internal/migrations/v3/common_test.go b/cmd/internal/migrations/v3/common_test.go index 858055a..d49a303 100644 --- a/cmd/internal/migrations/v3/common_test.go +++ b/cmd/internal/migrations/v3/common_test.go @@ -1,4 +1,4 @@ -package v3 +package v3_test import ( "bytes" @@ -7,6 +7,8 @@ import ( "strings" "testing" + migrations "github.com/gofiber/cli/cmd/internal/migrations" + v3 "github.com/gofiber/cli/cmd/internal/migrations/v3" "github.com/spf13/cobra" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -48,7 +50,7 @@ func handler(c *fiber.Ctx) error { return nil } var buf bytes.Buffer cmd := newCmd(&buf) - require.NoError(t, MigrateHandlerSignatures(cmd, dir, nil, nil)) + require.NoError(t, v3.MigrateHandlerSignatures(cmd, dir, nil, nil)) content := readFile(t, file) assert.NotContains(t, content, "*fiber.Ctx") @@ -77,7 +79,7 @@ func handler(c fiber.Ctx) error { var buf bytes.Buffer cmd := newCmd(&buf) - require.NoError(t, MigrateParserMethods(cmd, dir, nil, nil)) + require.NoError(t, v3.MigrateParserMethods(cmd, dir, nil, nil)) content := readFile(t, file) assert.Contains(t, content, ".Bind().Body(&v)") @@ -106,7 +108,7 @@ func handler(c fiber.Ctx) error { var buf bytes.Buffer cmd := newCmd(&buf) - require.NoError(t, MigrateRedirectMethods(cmd, dir, nil, nil)) + require.NoError(t, v3.MigrateRedirectMethods(cmd, dir, nil, nil)) content := readFile(t, file) assert.Contains(t, content, ".Redirect().To(\"/foo\")") @@ -135,7 +137,7 @@ func handler(c fiber.Ctx) error { var buf bytes.Buffer cmd := newCmd(&buf) - require.NoError(t, MigrateGenericHelpers(cmd, dir, nil, nil)) + require.NoError(t, v3.MigrateGenericHelpers(cmd, dir, nil, nil)) content := readFile(t, file) assert.Contains(t, content, "fiber.Params[int](c, \"id\"") @@ -165,7 +167,7 @@ func handler(c fiber.Ctx) error { var buf bytes.Buffer cmd := newCmd(&buf) - require.NoError(t, MigrateContextMethods(cmd, dir, nil, nil)) + require.NoError(t, v3.MigrateContextMethods(cmd, dir, nil, nil)) content := readFile(t, file) assert.Contains(t, content, ".RequestCtx()") @@ -189,7 +191,7 @@ func handler(c fiber.Ctx) error { var buf bytes.Buffer cmd := newCmd(&buf) - require.NoError(t, MigrateViewBind(cmd, dir, nil, nil)) + require.NoError(t, v3.MigrateViewBind(cmd, dir, nil, nil)) content := readFile(t, file) assert.Contains(t, content, ".ViewBind(") @@ -214,7 +216,7 @@ func handler(c fiber.Ctx) error { var buf bytes.Buffer cmd := newCmd(&buf) - require.NoError(t, MigrateAllParams(cmd, dir, nil, nil)) + require.NoError(t, v3.MigrateAllParams(cmd, dir, nil, nil)) content := readFile(t, file) assert.Contains(t, content, ".Bind().URI(&p)") @@ -238,7 +240,7 @@ func main() { var buf bytes.Buffer cmd := newCmd(&buf) - require.NoError(t, MigrateMount(cmd, dir, nil, nil)) + require.NoError(t, v3.MigrateMount(cmd, dir, nil, nil)) content := readFile(t, file) assert.Contains(t, content, ".Use(\"/api\", api)") @@ -262,7 +264,7 @@ func main() { var buf bytes.Buffer cmd := newCmd(&buf) - require.NoError(t, MigrateAddMethod(cmd, dir, nil, nil)) + require.NoError(t, v3.MigrateAddMethod(cmd, dir, nil, nil)) content := readFile(t, file) assert.Contains(t, content, `Add([]string{fiber.MethodGet}, "/foo"`) @@ -283,7 +285,7 @@ const mime = fiber.MIMEApplicationJavaScript var buf bytes.Buffer cmd := newCmd(&buf) - require.NoError(t, MigrateMimeConstants(cmd, dir, nil, nil)) + require.NoError(t, v3.MigrateMimeConstants(cmd, dir, nil, nil)) content := readFile(t, file) assert.NotContains(t, content, "MIMEApplicationJavaScript") @@ -307,7 +309,7 @@ var _ = logger.TagHeader var buf bytes.Buffer cmd := newCmd(&buf) - require.NoError(t, MigrateLoggerTags(cmd, dir, nil, nil)) + require.NoError(t, v3.MigrateLoggerTags(cmd, dir, nil, nil)) content := readFile(t, file) assert.NotContains(t, content, "TagHeader") @@ -332,7 +334,7 @@ func main() { var buf bytes.Buffer cmd := newCmd(&buf) - require.NoError(t, MigrateStaticRoutes(cmd, dir, nil, nil)) + require.NoError(t, v3.MigrateStaticRoutes(cmd, dir, nil, nil)) content := readFile(t, file) assert.Contains(t, content, `.Get("/*", static.New("./public"))`) @@ -359,7 +361,7 @@ func main() { var buf bytes.Buffer cmd := newCmd(&buf) - require.NoError(t, MigrateTrustedProxyConfig(cmd, dir, nil, nil)) + require.NoError(t, v3.MigrateTrustedProxyConfig(cmd, dir, nil, nil)) content := readFile(t, file) assert.Contains(t, content, "TrustProxy: true") @@ -385,7 +387,7 @@ var _ = cors.New(cors.Config{ var buf bytes.Buffer cmd := newCmd(&buf) - require.NoError(t, MigrateCORSConfig(cmd, dir, nil, nil)) + require.NoError(t, v3.MigrateCORSConfig(cmd, dir, nil, nil)) content := readFile(t, file) assert.Contains(t, content, `AllowOrigins: []string{"https://a.com", "https://b.com"}`) @@ -414,7 +416,7 @@ var _ = csrf.New(csrf.Config{ var buf bytes.Buffer cmd := newCmd(&buf) - require.NoError(t, MigrateCSRFConfig(cmd, dir, nil, nil)) + require.NoError(t, v3.MigrateCSRFConfig(cmd, dir, nil, nil)) content := readFile(t, file) assert.Contains(t, content, "IdleTimeout:") @@ -436,7 +438,7 @@ var _ = monitor.New()`) var buf bytes.Buffer cmd := newCmd(&buf) - require.NoError(t, MigrateMonitorImport(cmd, dir, nil, nil)) + require.NoError(t, v3.MigrateMonitorImport(cmd, dir, nil, nil)) content := readFile(t, file) assert.Contains(t, content, "github.com/gofiber/contrib/monitor") @@ -462,7 +464,7 @@ func main() { var buf bytes.Buffer cmd := newCmd(&buf) - require.NoError(t, MigrateProxyTLSConfig(cmd, dir, nil, nil)) + require.NoError(t, v3.MigrateProxyTLSConfig(cmd, dir, nil, nil)) content := readFile(t, file) assert.Contains(t, content, "proxy.WithClient(&fasthttp.Client{TLSConfig: &tls.Config{InsecureSkipVerify: true}})") @@ -488,7 +490,7 @@ func main() { var buf bytes.Buffer cmd := newCmd(&buf) - require.NoError(t, MigrateConfigListenerFields(cmd, dir, nil, nil)) + require.NoError(t, v3.MigrateConfigListenerFields(cmd, dir, nil, nil)) content := readFile(t, file) assert.Contains(t, content, "EnablePrefork: true") @@ -522,7 +524,7 @@ func main() { var buf bytes.Buffer cmd := newCmd(&buf) - require.NoError(t, MigrateListenerCallbacks(cmd, dir, nil, nil)) + require.NoError(t, v3.MigrateListenerCallbacks(cmd, dir, nil, nil)) content := readFile(t, file) assert.NotContains(t, content, "OnShutdownError") @@ -553,7 +555,7 @@ func main() { var buf bytes.Buffer cmd := newCmd(&buf) - require.NoError(t, MigrateListenMethods(cmd, dir, nil, nil)) + require.NoError(t, v3.MigrateListenMethods(cmd, dir, nil, nil)) content := readFile(t, file) assert.NotContains(t, content, "ListenTLS(") @@ -585,7 +587,7 @@ func main() { var buf bytes.Buffer cmd := newCmd(&buf) - require.NoError(t, MigrateFilesystemMiddleware(cmd, dir, nil, nil)) + require.NoError(t, v3.MigrateFilesystemMiddleware(cmd, dir, nil, nil)) content := readFile(t, file) assert.Contains(t, content, `static.New("", static.Config{`) @@ -609,7 +611,7 @@ var _ = envvar.New(envvar.Config{ var buf bytes.Buffer cmd := newCmd(&buf) - require.NoError(t, MigrateEnvVarConfig(cmd, dir, nil, nil)) + require.NoError(t, v3.MigrateEnvVarConfig(cmd, dir, nil, nil)) content := readFile(t, file) assert.NotContains(t, content, "ExcludeVars") @@ -636,7 +638,7 @@ var _ = limiter.New(limiter.Config{ var buf bytes.Buffer cmd := newCmd(&buf) - require.NoError(t, MigrateLimiterConfig(cmd, dir, nil, nil)) + require.NoError(t, v3.MigrateLimiterConfig(cmd, dir, nil, nil)) content := readFile(t, file) assert.Contains(t, content, "Expiration:") @@ -663,7 +665,7 @@ var _ = healthcheck.New(healthcheck.Config{ var buf bytes.Buffer cmd := newCmd(&buf) - require.NoError(t, MigrateHealthcheckConfig(cmd, dir, nil, nil)) + require.NoError(t, v3.MigrateHealthcheckConfig(cmd, dir, nil, nil)) content := readFile(t, file) assert.Contains(t, content, "Probe:") @@ -695,7 +697,7 @@ func main() { var buf bytes.Buffer cmd := newCmd(&buf) - require.NoError(t, MigrateAppTestConfig(cmd, dir, nil, nil)) + require.NoError(t, v3.MigrateAppTestConfig(cmd, dir, nil, nil)) content := readFile(t, file) assert.Contains(t, content, `app.Test(req, fiber.TestConfig{Timeout: 2*time.Second})`) @@ -719,7 +721,7 @@ func handler(c fiber.Ctx) error { var buf bytes.Buffer cmd := newCmd(&buf) - require.NoError(t, MigrateMiddlewareLocals(cmd, dir, nil, nil)) + require.NoError(t, v3.MigrateMiddlewareLocals(cmd, dir, nil, nil)) content := readFile(t, file) assert.Contains(t, content, `requestid.FromContext(c)`) @@ -743,7 +745,7 @@ func handler(c fiber.Ctx) error { var buf bytes.Buffer cmd := newCmd(&buf) - require.NoError(t, MigrateReqHeaderParser(cmd, dir, nil, nil)) + require.NoError(t, v3.MigrateReqHeaderParser(cmd, dir, nil, nil)) content := readFile(t, file) assert.Contains(t, content, `.Bind().Header(&v)`) @@ -768,7 +770,7 @@ var _ = session.New(session.Config{ var buf bytes.Buffer cmd := newCmd(&buf) - require.NoError(t, MigrateSessionConfig(cmd, dir, nil, nil)) + require.NoError(t, v3.MigrateSessionConfig(cmd, dir, nil, nil)) content := readFile(t, file) assert.Contains(t, content, "IdleTimeout:") @@ -796,7 +798,7 @@ require github.com/gofiber/fiber/v2 v2.0.0` var buf bytes.Buffer cmd := newCmd(&buf) - fn := MigrateGoVersion("1.23") + fn := migrations.MigrateGoVersion("1.23") require.NoError(t, fn(cmd, dir, nil, nil)) content := readFile(t, filepath.Join(dir, "go.mod")) From 310710f4071e2fa8ea300141b839440d82713953 Mon Sep 17 00:00:00 2001 From: RW Date: Sun, 13 Jul 2025 14:54:24 +0200 Subject: [PATCH 4/6] test: move generic Go version migration test --- cmd/internal/migrations/go_version_test.go | 66 ++++++++++++++++++++++ cmd/internal/migrations/v3/common_test.go | 32 ----------- 2 files changed, 66 insertions(+), 32 deletions(-) create mode 100644 cmd/internal/migrations/go_version_test.go diff --git a/cmd/internal/migrations/go_version_test.go b/cmd/internal/migrations/go_version_test.go new file mode 100644 index 0000000..741d968 --- /dev/null +++ b/cmd/internal/migrations/go_version_test.go @@ -0,0 +1,66 @@ +package migrations_test + +import ( + "bytes" + "os" + "path/filepath" + "testing" + + migrations "github.com/gofiber/cli/cmd/internal/migrations" + "github.com/spf13/cobra" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func writeTempFile(t *testing.T, dir, content string) string { + t.Helper() + path := filepath.Join(dir, "main.go") + err := os.WriteFile(path, []byte(content), 0o600) + require.NoError(t, err) + return path +} + +func readFile(t *testing.T, path string) string { + t.Helper() + b, err := os.ReadFile(path) // #nosec G304 + require.NoError(t, err) + return string(b) +} + +func newCmd(buf *bytes.Buffer) *cobra.Command { + cmd := &cobra.Command{} + cmd.SetOut(buf) + cmd.SetErr(buf) + return cmd +} + +func Test_MigrateGoVersion(t *testing.T) { + t.Parallel() + + dir, err := os.MkdirTemp("", "mgover") + require.NoError(t, err) + defer func() { require.NoError(t, os.RemoveAll(dir)) }() + + mod := `module example + +go 1.21 + +require github.com/gofiber/fiber/v2 v2.0.0` + require.NoError(t, os.WriteFile(filepath.Join(dir, "go.mod"), []byte(mod), 0o600)) + + vendor := filepath.Join(dir, "vendor") + require.NoError(t, os.Mkdir(vendor, 0o755)) + require.NoError(t, os.WriteFile(filepath.Join(vendor, "go.mod"), []byte("module vendor\n\ngo 1.10"), 0o600)) + + var buf bytes.Buffer + cmd := newCmd(&buf) + fn := migrations.MigrateGoVersion("1.23") + require.NoError(t, fn(cmd, dir, nil, nil)) + + content := readFile(t, filepath.Join(dir, "go.mod")) + assert.Contains(t, content, "go 1.23") + assert.Contains(t, buf.String(), "1.23") + + vendorContent := readFile(t, filepath.Join(vendor, "go.mod")) + assert.Contains(t, vendorContent, "go 1.10") +} diff --git a/cmd/internal/migrations/v3/common_test.go b/cmd/internal/migrations/v3/common_test.go index d49a303..65b99b0 100644 --- a/cmd/internal/migrations/v3/common_test.go +++ b/cmd/internal/migrations/v3/common_test.go @@ -7,7 +7,6 @@ import ( "strings" "testing" - migrations "github.com/gofiber/cli/cmd/internal/migrations" v3 "github.com/gofiber/cli/cmd/internal/migrations/v3" "github.com/spf13/cobra" "github.com/stretchr/testify/assert" @@ -777,34 +776,3 @@ var _ = session.New(session.Config{ assert.NotContains(t, content, "Expiration:") assert.Contains(t, buf.String(), "Migrating session middleware configs") } - -func Test_MigrateGoVersion(t *testing.T) { - t.Parallel() - - dir, err := os.MkdirTemp("", "mgover") - require.NoError(t, err) - defer func() { require.NoError(t, os.RemoveAll(dir)) }() - - mod := `module example - -go 1.21 - -require github.com/gofiber/fiber/v2 v2.0.0` - require.NoError(t, os.WriteFile(filepath.Join(dir, "go.mod"), []byte(mod), 0o600)) - - vendor := filepath.Join(dir, "vendor") - require.NoError(t, os.Mkdir(vendor, 0o755)) - require.NoError(t, os.WriteFile(filepath.Join(vendor, "go.mod"), []byte("module vendor\n\ngo 1.10"), 0o600)) - - var buf bytes.Buffer - cmd := newCmd(&buf) - fn := migrations.MigrateGoVersion("1.23") - require.NoError(t, fn(cmd, dir, nil, nil)) - - content := readFile(t, filepath.Join(dir, "go.mod")) - assert.Contains(t, content, "go 1.23") - assert.Contains(t, buf.String(), "1.23") - - vendorContent := readFile(t, filepath.Join(vendor, "go.mod")) - assert.Contains(t, vendorContent, "go 1.10") -} From 44d9343d12901360e1e0686a02bdd563aa99a5ba Mon Sep 17 00:00:00 2001 From: RW Date: Sun, 13 Jul 2025 15:10:54 +0200 Subject: [PATCH 5/6] Fix lint errors --- cmd/go_mod.go | 15 ++++++++------- cmd/internal/migrations/go_version.go | 14 +++++++------- cmd/internal/migrations/go_version_test.go | 12 ++---------- cmd/internal/migrations/v3/common_test.go | 2 +- cmd/migrate_test.go | 4 ++-- 5 files changed, 20 insertions(+), 27 deletions(-) diff --git a/cmd/go_mod.go b/cmd/go_mod.go index 9422a8f..de7c4d7 100644 --- a/cmd/go_mod.go +++ b/cmd/go_mod.go @@ -2,6 +2,7 @@ package cmd import ( "bytes" + "fmt" "io/fs" "os" "path/filepath" @@ -13,7 +14,7 @@ import ( func runGoMod(root string) error { dirs, err := fiberModuleDirs(root) if err != nil { - return err + return fmt.Errorf("find modules: %w", err) } for _, dir := range dirs { commands := [][]string{ @@ -22,7 +23,7 @@ func runGoMod(root string) error { {"go", "mod", "vendor"}, } for _, args := range commands { - cmd := execCommand(args[0], args[1:]...) + cmd := execCommand(args[0], args[1:]...) // #nosec G204 -- commands are controlled cmd.Dir = dir if err := runCmd(cmd); err != nil { return err @@ -36,7 +37,7 @@ func runGoMod(root string) error { // requires github.com/gofiber/fiber. vendor directories are skipped. func fiberModuleDirs(root string) ([]string, error) { var dirs []string - err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error { + walkErr := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error { if err != nil { return err } @@ -44,9 +45,9 @@ func fiberModuleDirs(root string) ([]string, error) { return filepath.SkipDir } if !d.IsDir() && d.Name() == "go.mod" { - b, err := os.ReadFile(path) + b, err := os.ReadFile(path) // #nosec G304 if err != nil { - return err + return fmt.Errorf("read %s: %w", path, err) } if bytes.Contains(b, []byte("github.com/gofiber/fiber")) { dirs = append(dirs, filepath.Dir(path)) @@ -54,8 +55,8 @@ func fiberModuleDirs(root string) ([]string, error) { } return nil }) - if err != nil { - return nil, err + if walkErr != nil { + return nil, fmt.Errorf("walk %s: %w", root, walkErr) } return dirs, nil } diff --git a/cmd/internal/migrations/go_version.go b/cmd/internal/migrations/go_version.go index d931d21..803fcf4 100644 --- a/cmd/internal/migrations/go_version.go +++ b/cmd/internal/migrations/go_version.go @@ -19,11 +19,11 @@ func MigrateGoVersion(minVersion string) func(*cobra.Command, string, *semver.Ve return func(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { dirs, err := fiberModuleDirs(cwd) if err != nil { - return err + return fmt.Errorf("find modules: %w", err) } for _, dir := range dirs { modFile := filepath.Join(dir, "go.mod") - b, err := os.ReadFile(modFile) + b, err := os.ReadFile(modFile) // #nosec G304 if err != nil { return fmt.Errorf("read %s: %w", modFile, err) } @@ -58,7 +58,7 @@ func MigrateGoVersion(minVersion string) func(*cobra.Command, string, *semver.Ve // requires github.com/gofiber/fiber. vendor directories are skipped. func fiberModuleDirs(root string) ([]string, error) { var dirs []string - err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error { + walkErr := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error { if err != nil { return err } @@ -66,9 +66,9 @@ func fiberModuleDirs(root string) ([]string, error) { return filepath.SkipDir } if !d.IsDir() && d.Name() == "go.mod" { - b, err := os.ReadFile(path) + b, err := os.ReadFile(path) // #nosec G304 if err != nil { - return err + return fmt.Errorf("read %s: %w", path, err) } if bytes.Contains(b, []byte("github.com/gofiber/fiber")) { dirs = append(dirs, filepath.Dir(path)) @@ -76,8 +76,8 @@ func fiberModuleDirs(root string) ([]string, error) { } return nil }) - if err != nil { - return nil, err + if walkErr != nil { + return nil, fmt.Errorf("walk %s: %w", root, walkErr) } return dirs, nil } diff --git a/cmd/internal/migrations/go_version_test.go b/cmd/internal/migrations/go_version_test.go index 741d968..3684cd8 100644 --- a/cmd/internal/migrations/go_version_test.go +++ b/cmd/internal/migrations/go_version_test.go @@ -6,20 +6,12 @@ import ( "path/filepath" "testing" - migrations "github.com/gofiber/cli/cmd/internal/migrations" + "github.com/gofiber/cli/cmd/internal/migrations" "github.com/spf13/cobra" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func writeTempFile(t *testing.T, dir, content string) string { - t.Helper() - path := filepath.Join(dir, "main.go") - err := os.WriteFile(path, []byte(content), 0o600) - require.NoError(t, err) - return path -} - func readFile(t *testing.T, path string) string { t.Helper() b, err := os.ReadFile(path) // #nosec G304 @@ -49,7 +41,7 @@ require github.com/gofiber/fiber/v2 v2.0.0` require.NoError(t, os.WriteFile(filepath.Join(dir, "go.mod"), []byte(mod), 0o600)) vendor := filepath.Join(dir, "vendor") - require.NoError(t, os.Mkdir(vendor, 0o755)) + require.NoError(t, os.Mkdir(vendor, 0o750)) require.NoError(t, os.WriteFile(filepath.Join(vendor, "go.mod"), []byte("module vendor\n\ngo 1.10"), 0o600)) var buf bytes.Buffer diff --git a/cmd/internal/migrations/v3/common_test.go b/cmd/internal/migrations/v3/common_test.go index 65b99b0..f538d21 100644 --- a/cmd/internal/migrations/v3/common_test.go +++ b/cmd/internal/migrations/v3/common_test.go @@ -7,7 +7,7 @@ import ( "strings" "testing" - v3 "github.com/gofiber/cli/cmd/internal/migrations/v3" + v3 "github.com/gofiber/cli/cmd/internal/migrations/v3" //nolint:revive // alias required "github.com/spf13/cobra" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/cmd/migrate_test.go b/cmd/migrate_test.go index a014f97..73de72f 100644 --- a/cmd/migrate_test.go +++ b/cmd/migrate_test.go @@ -19,14 +19,14 @@ require github.com/gofiber/fiber/v2 v2.0.0` require.NoError(t, os.WriteFile(filepath.Join(dir, "go.mod"), []byte(modContent), 0o600)) vendor := filepath.Join(dir, "vendor") - require.NoError(t, os.Mkdir(vendor, 0o755)) + require.NoError(t, os.Mkdir(vendor, 0o750)) require.NoError(t, os.WriteFile(filepath.Join(vendor, "go.mod"), []byte("module vendor"), 0o600)) origExec := execCommand var cmds []*exec.Cmd execCommand = func(name string, args ...string) *exec.Cmd { cs := append([]string{"-test.run=TestHelperProcess", "--", name}, args...) - cmd := exec.Command(os.Args[0], cs...) + cmd := exec.Command(os.Args[0], cs...) // #nosec G204 -- safe for test env := []string{"GO_WANT_HELPER_PROCESS=1"} if needError { env = append(env, "GO_WANT_HELPER_NEED_ERR=1") From 66b61b30cbb204373bd0e0876101790283253582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9?= Date: Sun, 13 Jul 2025 15:28:27 +0200 Subject: [PATCH 6/6] fix lint --- cmd/go_mod.go | 29 +++++++---- cmd/internal/migrations/go_version.go | 16 ++++-- cmd/migrate_test.go | 73 ++++++++++++++------------- go.mod | 1 + go.sum | 2 + 5 files changed, 71 insertions(+), 50 deletions(-) diff --git a/cmd/go_mod.go b/cmd/go_mod.go index de7c4d7..e8888b4 100644 --- a/cmd/go_mod.go +++ b/cmd/go_mod.go @@ -1,11 +1,13 @@ package cmd import ( - "bytes" "fmt" "io/fs" "os" "path/filepath" + "strings" + + "golang.org/x/mod/modfile" ) // runGoMod executes `go mod tidy`, `go mod download` and `go mod vendor` @@ -16,17 +18,17 @@ func runGoMod(root string) error { if err != nil { return fmt.Errorf("find modules: %w", err) } + commands := [][]string{ + {"go", "mod", "tidy"}, + {"go", "mod", "download"}, + {"go", "mod", "vendor"}, + } for _, dir := range dirs { - commands := [][]string{ - {"go", "mod", "tidy"}, - {"go", "mod", "download"}, - {"go", "mod", "vendor"}, - } for _, args := range commands { cmd := execCommand(args[0], args[1:]...) // #nosec G204 -- commands are controlled cmd.Dir = dir if err := runCmd(cmd); err != nil { - return err + return fmt.Errorf("in %s: %w", dir, err) } } } @@ -45,12 +47,19 @@ func fiberModuleDirs(root string) ([]string, error) { return filepath.SkipDir } if !d.IsDir() && d.Name() == "go.mod" { - b, err := os.ReadFile(path) // #nosec G304 + b, err := os.ReadFile(path) // #nosec G304 -- reading module file if err != nil { return fmt.Errorf("read %s: %w", path, err) } - if bytes.Contains(b, []byte("github.com/gofiber/fiber")) { - dirs = append(dirs, filepath.Dir(path)) + mf, err := modfile.Parse(path, b, nil) + if err != nil { + return fmt.Errorf("parse %s: %w", path, err) + } + for _, r := range mf.Require { + if strings.HasPrefix(r.Mod.Path, "github.com/gofiber/fiber") { + dirs = append(dirs, filepath.Dir(path)) + break + } } } return nil diff --git a/cmd/internal/migrations/go_version.go b/cmd/internal/migrations/go_version.go index 803fcf4..a893d29 100644 --- a/cmd/internal/migrations/go_version.go +++ b/cmd/internal/migrations/go_version.go @@ -1,13 +1,14 @@ package migrations import ( - "bytes" "fmt" "io/fs" "os" "path/filepath" "strings" + "golang.org/x/mod/modfile" + semver "github.com/Masterminds/semver/v3" "github.com/spf13/cobra" ) @@ -66,12 +67,19 @@ func fiberModuleDirs(root string) ([]string, error) { return filepath.SkipDir } if !d.IsDir() && d.Name() == "go.mod" { - b, err := os.ReadFile(path) // #nosec G304 + b, err := os.ReadFile(path) // #nosec G304 -- reading module file if err != nil { return fmt.Errorf("read %s: %w", path, err) } - if bytes.Contains(b, []byte("github.com/gofiber/fiber")) { - dirs = append(dirs, filepath.Dir(path)) + mf, err := modfile.Parse(path, b, nil) + if err != nil { + return fmt.Errorf("parse %s: %w", path, err) + } + for _, r := range mf.Require { + if strings.HasPrefix(r.Mod.Path, "github.com/gofiber/fiber") { + dirs = append(dirs, filepath.Dir(path)) + break + } } } return nil diff --git a/cmd/migrate_test.go b/cmd/migrate_test.go index 242b7aa..4f97694 100644 --- a/cmd/migrate_test.go +++ b/cmd/migrate_test.go @@ -70,6 +70,8 @@ func main() { defer func() { require.NoError(t, os.Chdir(cwd)) }() cmd := newMigrateCmd("go.mod") + setupCmd() + defer teardownCmd() out, err := runCobraCmd(cmd, "-t=3.0.0") require.NoError(t, err) @@ -96,44 +98,43 @@ func main() { at.Contains(out, "Migrating handler signatures") } - func Test_RunGoMod(t *testing.T) { - dir := t.TempDir() + dir := t.TempDir() - modContent := `module example + modContent := `module example require github.com/gofiber/fiber/v2 v2.0.0` - require.NoError(t, os.WriteFile(filepath.Join(dir, "go.mod"), []byte(modContent), 0o600)) - - vendor := filepath.Join(dir, "vendor") - require.NoError(t, os.Mkdir(vendor, 0o750)) - require.NoError(t, os.WriteFile(filepath.Join(vendor, "go.mod"), []byte("module vendor"), 0o600)) - - origExec := execCommand - var cmds []*exec.Cmd - execCommand = func(name string, args ...string) *exec.Cmd { - cs := append([]string{"-test.run=TestHelperProcess", "--", name}, args...) - cmd := exec.Command(os.Args[0], cs...) // #nosec G204 -- safe for test - env := []string{"GO_WANT_HELPER_PROCESS=1"} - if needError { - env = append(env, "GO_WANT_HELPER_NEED_ERR=1") - } - cmd.Env = env - cmds = append(cmds, cmd) - return cmd - } - defer func() { - execCommand = origExec - needError = false - }() - - require.NoError(t, runGoMod(dir)) - assert.Len(t, cmds, 3) - for _, c := range cmds { - assert.Equal(t, dir, c.Dir) - } - - cmds = nil - needError = true - assert.Error(t, runGoMod(dir)) + require.NoError(t, os.WriteFile(filepath.Join(dir, "go.mod"), []byte(modContent), 0o600)) + + vendor := filepath.Join(dir, "vendor") + require.NoError(t, os.Mkdir(vendor, 0o750)) + require.NoError(t, os.WriteFile(filepath.Join(vendor, "go.mod"), []byte("module vendor"), 0o600)) + + origExec := execCommand + var cmds []*exec.Cmd + execCommand = func(name string, args ...string) *exec.Cmd { + cs := append([]string{"-test.run=TestHelperProcess", "--", name}, args...) + cmd := exec.Command(os.Args[0], cs...) // #nosec G204 -- safe for test + env := []string{"GO_WANT_HELPER_PROCESS=1"} + if needError { + env = append(env, "GO_WANT_HELPER_NEED_ERR=1") + } + cmd.Env = env + cmds = append(cmds, cmd) + return cmd + } + defer func() { + execCommand = origExec + needError = false + }() + + require.NoError(t, runGoMod(dir)) + assert.Len(t, cmds, 3) + for _, c := range cmds { + assert.Equal(t, dir, c.Dir) + } + + cmds = nil + needError = true + assert.Error(t, runGoMod(dir)) } diff --git a/go.mod b/go.mod index ad22504..6c3fee4 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/muesli/termenv v0.16.0 github.com/spf13/cobra v1.9.1 github.com/stretchr/testify v1.10.0 + golang.org/x/mod v0.25.0 ) require ( diff --git a/go.sum b/go.sum index 4991c07..4b5e675 100644 --- a/go.sum +++ b/go.sum @@ -70,6 +70,8 @@ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavM github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E= golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= +golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=