From 4f1bcdd02125deaf6abab3240f5fb89f6b01c888 Mon Sep 17 00:00:00 2001 From: RW Date: Sun, 13 Jul 2025 13:57:19 +0200 Subject: [PATCH 1/4] Add integration test for v2 to v3 migration --- cmd/migrate_test.go | 103 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 cmd/migrate_test.go diff --git a/cmd/migrate_test.go b/cmd/migrate_test.go new file mode 100644 index 0000000..d95cd7c --- /dev/null +++ b/cmd/migrate_test.go @@ -0,0 +1,103 @@ +package cmd + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func readFileTB(t testing.TB, path string) string { + t.Helper() + b, err := os.ReadFile(path) + require.NoError(t, err) + return string(b) +} + +func Test_Migrate_V2_to_V3(t *testing.T) { + + dir, err := os.MkdirTemp("", "migrate_v2_v3") + require.NoError(t, err) + defer func() { require.NoError(t, os.RemoveAll(dir)) }() + + gomod := `module example.com/demo + +go 1.20 + +require github.com/gofiber/fiber/v2 v2.0.6 +` + require.NoError(t, os.WriteFile(filepath.Join(dir, "go.mod"), []byte(gomod), 0o600)) + + main := `package main +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/monitor" +) + +func handler(c *fiber.Ctx) error { + var v any + c.BodyParser(&v) + c.RedirectBack() + _ = c.ParamsInt("id", 0) + ctx := c.Context() + uc := c.UserContext() + c.SetUserContext(uc) + _ = ctx + return c.Bind("index", fiber.Map{}) +} + +func main() { + app := fiber.New(fiber.Config{ + EnableTrustedProxyCheck: true, + Prefork: true, + Network: "tcp", + }) + app.Static("/", "./public") + app.Add(fiber.MethodGet, "/foo", handler) + app.Mount("/api", app) + app.ListenTLS(":443", "cert.pem", "key.pem") + _ = fiber.MIMEApplicationJavaScript + _ = monitor.New() +} +` + require.NoError(t, os.WriteFile(filepath.Join(dir, "main.go"), []byte(main), 0o600)) + + cwd, err := os.Getwd() + require.NoError(t, err) + require.NoError(t, os.Chdir(dir)) + defer func() { require.NoError(t, os.Chdir(cwd)) }() + + origFile := currentVersionFile + currentVersionFile = "go.mod" + defer func() { currentVersionFile = origFile }() + + origTarget := targetVersionS + targetVersionS = "" + defer func() { targetVersionS = origTarget }() + + out, err := runCobraCmd(migrateCmd, "-t=3.0.0") + require.NoError(t, err) + + content := readFileTB(t, filepath.Join(dir, "main.go")) + at := assert.New(t) + at.Contains(content, "github.com/gofiber/fiber/v3") + at.Contains(content, "github.com/gofiber/fiber/v3/middleware/monitor") + at.NotContains(content, "*fiber.Ctx") + at.Contains(content, "fiber.Ctx") + at.Contains(content, ".ViewBind().Body(&v)") + at.Contains(content, ".Redirect().Back()") + at.Contains(content, "fiber.Params[int](c, \"id\"") + at.Contains(content, ".Use(\"/api\", app)") + at.Contains(content, ".Listen(") + at.Contains(content, "MIMETextJavaScript") + at.NotContains(content, "MIMEApplicationJavaScript") + + gm := readFileTB(t, filepath.Join(dir, "go.mod")) + at.Contains(gm, "github.com/gofiber/fiber/v3 v3.0.0") + + at.Contains(out, "Migration from Fiber 2.0.6 to 3.0.0") + at.Contains(out, "Migrating Go packages") + at.Contains(out, "Migrating handler signatures") +} From 73e217483357fae7d84346b3f45e9e4a81dcece2 Mon Sep 17 00:00:00 2001 From: RW Date: Sun, 13 Jul 2025 14:28:46 +0200 Subject: [PATCH 2/4] Fix lint issues in migration test --- cmd/migrate_test.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/cmd/migrate_test.go b/cmd/migrate_test.go index d95cd7c..eb342f1 100644 --- a/cmd/migrate_test.go +++ b/cmd/migrate_test.go @@ -9,15 +9,14 @@ import ( "github.com/stretchr/testify/require" ) -func readFileTB(t testing.TB, path string) string { - t.Helper() - b, err := os.ReadFile(path) - require.NoError(t, err) +func readFileTB(tb testing.TB, path string) string { + tb.Helper() + b, err := os.ReadFile(filepath.Clean(path)) + require.NoError(tb, err) return string(b) } func Test_Migrate_V2_to_V3(t *testing.T) { - dir, err := os.MkdirTemp("", "migrate_v2_v3") require.NoError(t, err) defer func() { require.NoError(t, os.RemoveAll(dir)) }() From ca4da5b727df9d1aeb33e5bf0055d7c0b42039ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9?= Date: Sun, 13 Jul 2025 14:53:28 +0200 Subject: [PATCH 3/4] fix --- cmd/internal/migrations/lists.go | 3 +- cmd/internal/migrations/v3/common.go | 70 ++++++++--------------- cmd/internal/migrations/v3/common_test.go | 27 +-------- cmd/migrate_test.go | 7 ++- 4 files changed, 30 insertions(+), 77 deletions(-) diff --git a/cmd/internal/migrations/lists.go b/cmd/internal/migrations/lists.go index d6a4a34..0bedcfa 100644 --- a/cmd/internal/migrations/lists.go +++ b/cmd/internal/migrations/lists.go @@ -29,8 +29,8 @@ var Migrations = []Migration{ To: "<4.0.0-0", Functions: []MigrationFn{ v3migrations.MigrateHandlerSignatures, + v3migrations.MigrateViewBind, v3migrations.MigrateParserMethods, - v3migrations.MigrateAllParams, v3migrations.MigrateRedirectMethods, v3migrations.MigrateGenericHelpers, v3migrations.MigrateAddMethod, @@ -43,7 +43,6 @@ var Migrations = []Migration{ v3migrations.MigrateListenerCallbacks, v3migrations.MigrateListenMethods, v3migrations.MigrateContextMethods, - v3migrations.MigrateViewBind, v3migrations.MigrateCORSConfig, v3migrations.MigrateCSRFConfig, v3migrations.MigrateMonitorImport, diff --git a/cmd/internal/migrations/v3/common.go b/cmd/internal/migrations/v3/common.go index 688dbbd..d82f41b 100644 --- a/cmd/internal/migrations/v3/common.go +++ b/cmd/internal/migrations/v3/common.go @@ -69,17 +69,14 @@ func MigrateRedirectMethods(cmd *cobra.Command, cwd string, _, _ *semver.Version // MigrateGenericHelpers migrates helper functions that now use generics func MigrateGenericHelpers(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { + reParamsInt := regexp.MustCompile(`(\w+)\.ParamsInt\(`) + reQueryInt := regexp.MustCompile(`(\w+)\.QueryInt\(`) + reQueryFloat := regexp.MustCompile(`(\w+)\.QueryFloat\(`) + reQueryBool := regexp.MustCompile(`(\w+)\.QueryBool\(`) err := internal.ChangeFileContent(cwd, func(content string) string { - reParamsInt := regexp.MustCompile(`(\w+)\.ParamsInt\(`) content = reParamsInt.ReplaceAllString(content, "fiber.Params[int]($1, ") - - reQueryInt := regexp.MustCompile(`(\w+)\.QueryInt\(`) content = reQueryInt.ReplaceAllString(content, "fiber.Query[int]($1, ") - - reQueryFloat := regexp.MustCompile(`(\w+)\.QueryFloat\(`) content = reQueryFloat.ReplaceAllString(content, "fiber.Query[float64]($1, ") - - reQueryBool := regexp.MustCompile(`(\w+)\.QueryBool\(`) content = reQueryBool.ReplaceAllString(content, "fiber.Query[bool]($1, ") return content @@ -113,10 +110,11 @@ func MigrateContextMethods(cmd *cobra.Command, cwd string, _, _ *semver.Version) // MigrateViewBind replaces the old Ctx.Bind view binding helper with ViewBind func MigrateViewBind(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { - replacer := strings.NewReplacer(".Bind(", ".ViewBind(") + // Replace .Bind() with arguments, not the Bind() from the binding package + reViewBind := regexp.MustCompile(`\.Bind\(([^)]+)\)`) err := internal.ChangeFileContent(cwd, func(content string) string { - return replacer.Replace(content) + return reViewBind.ReplaceAllString(content, ".ViewBind($1)") }) if err != nil { return fmt.Errorf("failed to migrate ViewBind calls: %w", err) @@ -126,23 +124,6 @@ func MigrateViewBind(cmd *cobra.Command, cwd string, _, _ *semver.Version) error return nil } -// MigrateAllParams replaces deprecated AllParams helper with the new binding API -func MigrateAllParams(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { - replacer := strings.NewReplacer( - ".AllParams(", ".Bind().URI(", - ) - - err := internal.ChangeFileContent(cwd, func(content string) string { - return replacer.Replace(content) - }) - if err != nil { - return fmt.Errorf("failed to migrate AllParams: %w", err) - } - - cmd.Println("Migrating AllParams") - return nil -} - // MigrateMount replaces app.Mount with app.Use func MigrateMount(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { replacer := strings.NewReplacer(".Mount(", ".Use(") @@ -160,8 +141,9 @@ func MigrateMount(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { // MigrateAddMethod adapts the Add method signature func MigrateAddMethod(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { + re := regexp.MustCompile(`\.Add\(\s*([^,\n]+)\s*,`) + err := internal.ChangeFileContent(cwd, func(content string) string { - re := regexp.MustCompile(`\.Add\(\s*([^,\n]+)\s*,`) return re.ReplaceAllString(content, ".Add([]string{$1},") }) if err != nil { @@ -174,6 +156,11 @@ func MigrateAddMethod(cmd *cobra.Command, cwd string, _, _ *semver.Version) erro // MigrateCORSConfig updates cors middleware configuration fields func MigrateCORSConfig(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { + reOrigins := regexp.MustCompile(`AllowOrigins:\s*"([^"]*)"`) + reMethods := regexp.MustCompile(`AllowMethods:\s*"([^"]*)"`) + reHeaders := regexp.MustCompile(`AllowHeaders:\s*"([^"]*)"`) + reExpose := regexp.MustCompile(`ExposeHeaders:\s*"([^"]*)"`) + err := internal.ChangeFileContent(cwd, func(content string) string { conv := func(src string, re *regexp.Regexp, field string) string { return re.ReplaceAllStringFunc(src, func(s string) string { @@ -189,16 +176,9 @@ func MigrateCORSConfig(cmd *cobra.Command, cwd string, _, _ *semver.Version) err }) } - reOrigins := regexp.MustCompile(`AllowOrigins:\s*"([^"]*)"`) content = conv(content, reOrigins, "AllowOrigins") - - reMethods := regexp.MustCompile(`AllowMethods:\s*"([^"]*)"`) content = conv(content, reMethods, "AllowMethods") - - reHeaders := regexp.MustCompile(`AllowHeaders:\s*"([^"]*)"`) content = conv(content, reHeaders, "AllowHeaders") - - reExpose := regexp.MustCompile(`ExposeHeaders:\s*"([^"]*)"`) content = conv(content, reExpose, "ExposeHeaders") return content @@ -214,10 +194,9 @@ func MigrateCORSConfig(cmd *cobra.Command, cwd string, _, _ *semver.Version) err // MigrateCSRFConfig updates csrf middleware configuration fields func MigrateCSRFConfig(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { replacer := strings.NewReplacer("Expiration:", "IdleTimeout:") - + re := regexp.MustCompile(`\s*SessionKey:\s*[^,]+,?\n`) err := internal.ChangeFileContent(cwd, func(content string) string { content = replacer.Replace(content) - re := regexp.MustCompile(`\s*SessionKey:\s*[^,]+,?\n`) return re.ReplaceAllString(content, "") }) if err != nil { @@ -230,10 +209,9 @@ func MigrateCSRFConfig(cmd *cobra.Command, cwd string, _, _ *semver.Version) err // MigrateMonitorImport updates monitor middleware import path func MigrateMonitorImport(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { + re := regexp.MustCompile(`github\.com/gofiber/fiber/([^/]+)/middleware/monitor`) err := internal.ChangeFileContent(cwd, func(content string) string { - return strings.ReplaceAll(content, - "github.com/gofiber/fiber/v2/middleware/monitor", - "github.com/gofiber/contrib/monitor") + return re.ReplaceAllString(content, "github.com/gofiber/contrib/monitor") }) if err != nil { return fmt.Errorf("failed to migrate monitor import: %w", err) @@ -245,8 +223,8 @@ func MigrateMonitorImport(cmd *cobra.Command, cwd string, _, _ *semver.Version) // MigrateProxyTLSConfig updates proxy TLS helper to new client configuration func MigrateProxyTLSConfig(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { + re := regexp.MustCompile(`proxy\.WithTlsConfig\(([^)]+)\)`) err := internal.ChangeFileContent(cwd, func(content string) string { - re := regexp.MustCompile(`proxy\.WithTlsConfig\(([^)]+)\)`) return re.ReplaceAllString(content, "proxy.WithClient(&fasthttp.Client{TLSConfig: $1})") }) @@ -336,11 +314,10 @@ func MigrateStaticRoutes(cmd *cobra.Command, cwd string, _, _ *semver.Version) e // MigrateTrustedProxyConfig updates trusted proxy configuration options func MigrateTrustedProxyConfig(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { + reEnable := regexp.MustCompile(`EnableTrustedProxyCheck`) + reProxies := regexp.MustCompile(`TrustedProxies:\s*([^,\n]+),`) err := internal.ChangeFileContent(cwd, func(content string) string { - reEnable := regexp.MustCompile(`EnableTrustedProxyCheck`) content = reEnable.ReplaceAllString(content, "TrustProxy") - - reProxies := regexp.MustCompile(`TrustedProxies:\s*([^,\n]+),`) content = reProxies.ReplaceAllString(content, "TrustProxyConfig: fiber.TrustProxyConfig{Proxies: $1},") return content @@ -375,11 +352,10 @@ func MigrateConfigListenerFields(cmd *cobra.Command, cwd string, _, _ *semver.Ve // MigrateListenerCallbacks removes deprecated OnShutdown callbacks from // ListenerConfig. Fiber v3 replaces these with the OnPostShutdown hook. func MigrateListenerCallbacks(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { + reErr := regexp.MustCompile(`\s*OnShutdownError:\s*[^,]+,?\n`) + reSuccess := regexp.MustCompile(`\s*OnShutdownSuccess:\s*[^,]+,?\n`) err := internal.ChangeFileContent(cwd, func(content string) string { - reErr := regexp.MustCompile(`\s*OnShutdownError:\s*[^,]+,?\n`) content = reErr.ReplaceAllString(content, "") - - reSuccess := regexp.MustCompile(`\s*OnShutdownSuccess:\s*[^,]+,?\n`) content = reSuccess.ReplaceAllString(content, "") return content @@ -428,8 +404,8 @@ func MigrateFilesystemMiddleware(cmd *cobra.Command, cwd string, _, _ *semver.Ve // MigrateEnvVarConfig removes deprecated ExcludeVars field from envvar middleware configuration func MigrateEnvVarConfig(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { + re := regexp.MustCompile(`\s*ExcludeVars:\s*[^,]+,?\n`) err := internal.ChangeFileContent(cwd, func(content string) string { - re := regexp.MustCompile(`\s*ExcludeVars:\s*[^,]+,?\n`) return re.ReplaceAllString(content, "") }) if err != nil { diff --git a/cmd/internal/migrations/v3/common_test.go b/cmd/internal/migrations/v3/common_test.go index 55c42ba..7b4d7ca 100644 --- a/cmd/internal/migrations/v3/common_test.go +++ b/cmd/internal/migrations/v3/common_test.go @@ -71,6 +71,7 @@ func handler(c fiber.Ctx) error { c.CookieParser(&v) c.ParamsParser(&v) c.QueryParser(&v) + c.AllParams(&p) return nil } `) @@ -184,7 +185,7 @@ func Test_MigrateViewBind(t *testing.T) { file := writeTempFile(t, dir, `package main import "github.com/gofiber/fiber/v2" func handler(c fiber.Ctx) error { - return c.Bind("index", fiber.Map{}) + return c.Bind(fiber.Map{}) }`) var buf bytes.Buffer @@ -197,30 +198,6 @@ func handler(c fiber.Ctx) error { assert.Contains(t, buf.String(), "Migrating view binding helpers") } -func Test_MigrateAllParams(t *testing.T) { - t.Parallel() - - dir, err := os.MkdirTemp("", "maptest") - require.NoError(t, err) - defer func() { require.NoError(t, os.RemoveAll(dir)) }() - - file := writeTempFile(t, dir, `package main -import "github.com/gofiber/fiber/v2" -func handler(c fiber.Ctx) error { - var p any - c.AllParams(&p) - return nil -}`) - - var buf bytes.Buffer - cmd := newCmd(&buf) - require.NoError(t, MigrateAllParams(cmd, dir, nil, nil)) - - content := readFile(t, file) - assert.Contains(t, content, ".Bind().URI(&p)") - assert.Contains(t, buf.String(), "Migrating AllParams") -} - func Test_MigrateMount(t *testing.T) { t.Parallel() diff --git a/cmd/migrate_test.go b/cmd/migrate_test.go index eb342f1..eb7d19f 100644 --- a/cmd/migrate_test.go +++ b/cmd/migrate_test.go @@ -44,7 +44,7 @@ func handler(c *fiber.Ctx) error { uc := c.UserContext() c.SetUserContext(uc) _ = ctx - return c.Bind("index", fiber.Map{}) + return c.Bind(fiber.Map{}) } func main() { @@ -82,10 +82,11 @@ func main() { content := readFileTB(t, filepath.Join(dir, "main.go")) at := assert.New(t) at.Contains(content, "github.com/gofiber/fiber/v3") - at.Contains(content, "github.com/gofiber/fiber/v3/middleware/monitor") + at.Contains(content, "github.com/gofiber/contrib/monitor") at.NotContains(content, "*fiber.Ctx") at.Contains(content, "fiber.Ctx") - at.Contains(content, ".ViewBind().Body(&v)") + at.Contains(content, ".Bind().Body(&v)") + at.Contains(content, ".ViewBind(fiber.Map{})") at.Contains(content, ".Redirect().Back()") at.Contains(content, "fiber.Params[int](c, \"id\"") at.Contains(content, ".Use(\"/api\", app)") From 851ccb7ff54486bed08b2cbd6ea3898a9fa7397d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9?= Date: Sun, 13 Jul 2025 15:09:48 +0200 Subject: [PATCH 4/4] fix lint --- cmd/migrate.go | 32 ++++++++++++++++++++------------ cmd/migrate_test.go | 11 ++--------- cmd/version.go | 9 +++++++-- 3 files changed, 29 insertions(+), 23 deletions(-) diff --git a/cmd/migrate.go b/cmd/migrate.go index a9508a9..6d1872f 100644 --- a/cmd/migrate.go +++ b/cmd/migrate.go @@ -6,33 +6,41 @@ import ( "strings" "github.com/Masterminds/semver/v3" - "github.com/gofiber/cli/cmd/internal/migrations" "github.com/muesli/termenv" "github.com/spf13/cobra" + + "github.com/gofiber/cli/cmd/internal/migrations" ) -var targetVersionS string +func newMigrateCmd(currentVersionFile string) *cobra.Command { + var targetVersionS string + + cmd := &cobra.Command{ + Use: "migrate", + Short: "Migrate Fiber project version to a newer version", + } -func init() { latestFiberVersion, err := LatestFiberVersion() if err != nil { latestFiberVersion = "" } - migrateCmd.Flags().StringVarP(&targetVersionS, "to", "t", "", "Migrate to a specific version e.g:"+latestFiberVersion+" Format: X.Y.Z") - if err := migrateCmd.MarkFlagRequired("to"); err != nil { + cmd.Flags().StringVarP(&targetVersionS, "to", "t", "", "Migrate to a specific version e.g:"+latestFiberVersion+" Format: X.Y.Z") + if err := cmd.MarkFlagRequired("to"); err != nil { panic(err) } -} -var migrateCmd = &cobra.Command{ - Use: "migrate", - Short: "Migrate Fiber project version to a newer version", - RunE: migrateRunE, + cmd.RunE = func(cmd *cobra.Command, _ []string) error { + return migrateRunE(cmd, currentVersionFile, targetVersionS) + } + + return cmd } -func migrateRunE(cmd *cobra.Command, _ []string) error { - currentVersionS, err := currentVersion() +var migrateCmd = newMigrateCmd("go.mod") + +func migrateRunE(cmd *cobra.Command, currentVersionFile, targetVersionS string) error { + currentVersionS, err := currentVersionFromFile(currentVersionFile) if err != nil { return fmt.Errorf("current fiber project version not found: %w", err) } diff --git a/cmd/migrate_test.go b/cmd/migrate_test.go index eb7d19f..9df2761 100644 --- a/cmd/migrate_test.go +++ b/cmd/migrate_test.go @@ -68,15 +68,8 @@ func main() { require.NoError(t, os.Chdir(dir)) defer func() { require.NoError(t, os.Chdir(cwd)) }() - origFile := currentVersionFile - currentVersionFile = "go.mod" - defer func() { currentVersionFile = origFile }() - - origTarget := targetVersionS - targetVersionS = "" - defer func() { targetVersionS = origTarget }() - - out, err := runCobraCmd(migrateCmd, "-t=3.0.0") + cmd := newMigrateCmd("go.mod") + out, err := runCobraCmd(cmd, "-t=3.0.0") require.NoError(t, err) content := readFileTB(t, filepath.Join(dir, "main.go")) diff --git a/cmd/version.go b/cmd/version.go index 67133bd..b6954e7 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -7,6 +7,7 @@ import ( "io" "net/http" "os" + "path/filepath" "regexp" "time" @@ -43,8 +44,8 @@ var ( currentVersionFile = "go.mod" ) -func currentVersion() (string, error) { - b, err := os.ReadFile(currentVersionFile) +func currentVersionFromFile(path string) (string, error) { + b, err := os.ReadFile(filepath.Clean(path)) if err != nil { return "", fmt.Errorf("read current version file: %w", err) } @@ -56,6 +57,10 @@ func currentVersion() (string, error) { return "", errors.New("github.com/gofiber/fiber was not found in go.mod") } +func currentVersion() (string, error) { + return currentVersionFromFile(currentVersionFile) +} + var latestVersionRegexp = regexp.MustCompile(`"name":\s*?"v(.*?)"`) // LatestFiberVersion retrieves the most recent Fiber release version from GitHub.