From 549b5c7a6282e0c19c700fd4fc779dd366466899 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9?= Date: Thu, 18 Apr 2024 18:46:06 +0200 Subject: [PATCH 01/27] add command for migration purpose --- .gitignore | 1 + development.md | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 development.md diff --git a/.gitignore b/.gitignore index 829ad8a..070eb2c 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ *.test *.out .idea/ +vendor diff --git a/development.md b/development.md new file mode 100644 index 0000000..08b2437 --- /dev/null +++ b/development.md @@ -0,0 +1,17 @@ +# Local Development + +Install [reflex](github.com/cespare/reflex) +```bash +go install github.com/cespare/reflex@latest +``` + +Use reflex to watch for changes in the project and recompile the binary +```bash +reflex -r '\.go$' -- go install ./fiber +``` + +Test the binary in fiber project +```bash +cd my-fiber-project +fiber version +``` \ No newline at end of file From 057bbd9d71ce976f52cee1983ffbe57e5a22617d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9?= Date: Thu, 18 Apr 2024 19:08:30 +0200 Subject: [PATCH 02/27] add command for migration purpose --- cmd/migrate.go | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++ cmd/root.go | 2 +- 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 cmd/migrate.go diff --git a/cmd/migrate.go b/cmd/migrate.go new file mode 100644 index 0000000..a764b79 --- /dev/null +++ b/cmd/migrate.go @@ -0,0 +1,57 @@ +package cmd + +import ( + "fmt" + "os" + + "github.com/gofiber/cli/cmd/internal" + "github.com/muesli/termenv" + "github.com/spf13/cobra" +) + +var migrateCmd = &cobra.Command{ + Use: "migrate", + Short: "Migrate Fiber project version to a newer version", + RunE: migrateRunE, +} + +var migrated bool + +// TODO: fetch current version from go.mod +// TODO: use the --to flag to specify the version to migrate +// TODO: build migration scripts in a separate folder + +func migrateRunE(cmd *cobra.Command, _ []string) error { + cliLatestVersion, err := latestVersion(true) + if err != nil { + return err + } + + if version != cliLatestVersion { + migrate(cmd, cliLatestVersion) + } else { + msg := fmt.Sprintf("Currently Fiber cli is the latest version %s.", cliLatestVersion) + cmd.Println(termenv.String(msg). + Foreground(termenv.ANSIBrightBlue)) + } + + return nil +} + +func migrate(cmd *cobra.Command, cliLatestVersion string) { + migrater := execCommand("go", "get", "-u", "-v", "github.com/gofiber/cli/fiber") + migrater.Env = append(migrater.Env, os.Environ()...) + migrater.Env = append(migrater.Env, "GO111MODULE=off") + + scmd := internal.NewSpinnerCmd(migrater, "Upgrading") + + if err := scmd.Run(); err != nil && !skipSpinner { + cmd.Printf("fiber: failed to migrate: %v", err) + return + } + + success := fmt.Sprintf("Done! Fiber cli is now at v%s!", cliLatestVersion) + cmd.Println(termenv.String(success).Foreground(termenv.ANSIBrightGreen)) + + migrated = true +} diff --git a/cmd/root.go b/cmd/root.go index 2d5d16d..30cd557 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -25,7 +25,7 @@ type rootConfig struct { func init() { rootCmd.AddCommand( - versionCmd, newCmd, devCmd, upgradeCmd, + versionCmd, newCmd, devCmd, upgradeCmd, migrateCmd, ) } From 29b596d0b469feb560f91100e74bcf0ffb8f1567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9?= Date: Sun, 21 Apr 2024 21:28:19 +0200 Subject: [PATCH 03/27] add command for migration purpose --- cmd/internal/helpers.go | 41 +++++++++++++++++ cmd/internal/migrations/common.go | 53 +++++++++++++++++++++ cmd/internal/migrations/lists.go | 54 ++++++++++++++++++++++ cmd/internal/migrations/v3/common.go | 25 ++++++++++ cmd/migrate.go | 69 +++++++++++++++------------- cmd/version.go | 7 +-- development.md | 8 ++-- go.mod | 1 + go.sum | 2 + 9 files changed, 222 insertions(+), 38 deletions(-) create mode 100644 cmd/internal/migrations/common.go create mode 100644 cmd/internal/migrations/lists.go create mode 100644 cmd/internal/migrations/v3/common.go diff --git a/cmd/internal/helpers.go b/cmd/internal/helpers.go index 3453ca9..77f81e3 100644 --- a/cmd/internal/helpers.go +++ b/cmd/internal/helpers.go @@ -2,6 +2,9 @@ package internal import ( "fmt" + "os" + "path/filepath" + "strings" tea "github.com/charmbracelet/bubbletea" "github.com/containerd/console" @@ -27,3 +30,41 @@ func errCmd(err error) tea.Cmd { return finishedMsg{err} } } + +type FileProcessor func(content string) string + +func ChangeFileContent(cwd string, processorFn FileProcessor) error { + // change go files in project + err := filepath.Walk(cwd, func(path string, info os.FileInfo, err error) error { + if err != nil { + //fmt.Printf("Error while traversing %s: %v\n", path, err) + return err + } + + // Skip directories named "vendor" + if info.IsDir() && info.Name() == "vendor" { + //fmt.Printf("Skipping directory: %s\n", path) + return filepath.SkipDir + } + + // Check if the file is a Go file (ending with ".go") + if info.IsDir() || !strings.HasSuffix(info.Name(), ".go") { + return nil + } + //fmt.Printf("Processing Go file: %s\n", path) + fileContent, err := os.ReadFile(path) + + // update go.mod file + if err2 := os.WriteFile(path, []byte(processorFn(string(fileContent))), 0644); err != nil { + return err2 + } + + return nil + }) + + if err != nil { + return fmt.Errorf("Error while traversing the directory tree: %v\n", err) + } + + return nil +} diff --git a/cmd/internal/migrations/common.go b/cmd/internal/migrations/common.go new file mode 100644 index 0000000..da2dca4 --- /dev/null +++ b/cmd/internal/migrations/common.go @@ -0,0 +1,53 @@ +package migrations + +import ( + "fmt" + "github.com/Masterminds/semver/v3" + "github.com/spf13/cobra" + "os" + "regexp" + "strconv" + "strings" + + "github.com/gofiber/cli/cmd/internal" +) + +var ( + pkgRegex = regexp.MustCompile(`(github\.com\/gofiber\/fiber\/)(v\d+)( *?)(v[\w.-]+)`) +) + +func MigrateGoPkgs(cmd *cobra.Command, cwd string, curr *semver.Version, target *semver.Version) error { + pkgReplacer := strings.NewReplacer( + "github.com/gofiber/fiber/v"+strconv.FormatUint(curr.Major(), 10), + "github.com/gofiber/fiber/v"+strconv.FormatUint(target.Major(), 10), + ) + + err := internal.ChangeFileContent(cwd, func(content string) string { + return pkgReplacer.Replace(content) + }) + if err != nil { + return fmt.Errorf("failed to migrate Go packages: %v", err) + } + + // get go.mod file + modFile := "go.mod" + fileContent, err := os.ReadFile(modFile) + if err != nil { + return err + } + + // replace old version with new version in go.mod file + fileContentStr := pkgRegex.ReplaceAllString( + string(fileContent), + "${1}v"+strconv.FormatUint(target.Major(), 10)+"${3}v"+target.String(), + ) + + // update go.mod file + if err := os.WriteFile(modFile, []byte(fileContentStr), 0644); err != nil { + return err + } + + cmd.Println("Migrating Go packages") + + return nil +} diff --git a/cmd/internal/migrations/lists.go b/cmd/internal/migrations/lists.go new file mode 100644 index 0000000..b988d3e --- /dev/null +++ b/cmd/internal/migrations/lists.go @@ -0,0 +1,54 @@ +package migrations + +import ( + "fmt" + "github.com/Masterminds/semver/v3" + "github.com/spf13/cobra" + + "github.com/gofiber/cli/cmd/internal/migrations/v3" +) + +// MigrationFn is a function that will be called during migration +type MigrationFn func(cmd *cobra.Command, cwd string, curr *semver.Version, target *semver.Version) error + +// Migration is a single migration +type Migration struct { + From string + To string + Functions []MigrationFn +} + +// Migrations is a list of all migrations +// Example structure: +// {"from": ">=2.0.0", "to": "<=3.*.*", "fn": [MigrateFN, MigrateFN]} +var Migrations = []Migration{ + {From: ">=2.0.0", To: "<4.0.0-0", Functions: []MigrationFn{v3.MigrateHandlerSignatures}}, + {From: ">=1.0.0", To: ">=0.0.0-0", Functions: []MigrationFn{MigrateGoPkgs}}, +} + +// DoMigration runs all migrations +// It will run all migrations that match the current and target version +func DoMigration(cmd *cobra.Command, cwd string, curr *semver.Version, target *semver.Version) error { + for _, m := range Migrations { + toC, err := semver.NewConstraint(m.To) + if err != nil { + return err + } + fromC, err := semver.NewConstraint(m.From) + if err != nil { + return err + } + + if fromC.Check(curr) && toC.Check(target) { + for _, fn := range m.Functions { + if err := fn(cmd, cwd, curr, target); err != nil { + return err + } + } + } else { + fmt.Printf("Skipping migration from %s to %s\n", m.From, m.To) + } + } + + return nil +} diff --git a/cmd/internal/migrations/v3/common.go b/cmd/internal/migrations/v3/common.go new file mode 100644 index 0000000..d1d35bf --- /dev/null +++ b/cmd/internal/migrations/v3/common.go @@ -0,0 +1,25 @@ +package v3 + +import ( + "fmt" + "github.com/Masterminds/semver/v3" + "github.com/spf13/cobra" + "strings" + + "github.com/gofiber/cli/cmd/internal" +) + +func MigrateHandlerSignatures(cmd *cobra.Command, cwd string, _ *semver.Version, _ *semver.Version) error { + sigReplacer := strings.NewReplacer("*fiber.Ctx", "fiber.Ctx") + + err := internal.ChangeFileContent(cwd, func(content string) string { + return sigReplacer.Replace(content) + }) + if err != nil { + return fmt.Errorf("failed to migrate handler signatures: %v", err) + } + + cmd.Println("Migrating handler signatures") + + return nil +} diff --git a/cmd/migrate.go b/cmd/migrate.go index a764b79..b46f3f0 100644 --- a/cmd/migrate.go +++ b/cmd/migrate.go @@ -2,56 +2,63 @@ package cmd import ( "fmt" - "os" - - "github.com/gofiber/cli/cmd/internal" + "github.com/Masterminds/semver/v3" + "github.com/gofiber/cli/cmd/internal/migrations" "github.com/muesli/termenv" "github.com/spf13/cobra" + "os" + "strings" +) + +var ( + targetVersionS string + latestFiberVersion string ) +func init() { + latestFiberVersion, _ := latestVersion(false) + + migrateCmd.Flags().StringVarP(&targetVersionS, "to", "t", "", "Migrate to a specific version e.g: "+latestFiberVersion+" Format: X.Y.Z") + migrateCmd.MarkFlagRequired("to") +} + var migrateCmd = &cobra.Command{ Use: "migrate", Short: "Migrate Fiber project version to a newer version", RunE: migrateRunE, } -var migrated bool - -// TODO: fetch current version from go.mod -// TODO: use the --to flag to specify the version to migrate -// TODO: build migration scripts in a separate folder - func migrateRunE(cmd *cobra.Command, _ []string) error { - cliLatestVersion, err := latestVersion(true) + currentVersionS, err := currentVersion() if err != nil { - return err + return fmt.Errorf("current fiber project version not found: %v", err) } + currentVersionS = strings.TrimPrefix(currentVersionS, "v") + currentVersion := semver.MustParse(currentVersionS) - if version != cliLatestVersion { - migrate(cmd, cliLatestVersion) - } else { - msg := fmt.Sprintf("Currently Fiber cli is the latest version %s.", cliLatestVersion) - cmd.Println(termenv.String(msg). - Foreground(termenv.ANSIBrightBlue)) + targetVersionS = strings.TrimPrefix(targetVersionS, "v") + targetVersion, err := semver.NewVersion(targetVersionS) + if err != nil { + return fmt.Errorf("invalid version for \"%s\": %v", targetVersionS, err) } - return nil -} - -func migrate(cmd *cobra.Command, cliLatestVersion string) { - migrater := execCommand("go", "get", "-u", "-v", "github.com/gofiber/cli/fiber") - migrater.Env = append(migrater.Env, os.Environ()...) - migrater.Env = append(migrater.Env, "GO111MODULE=off") + if !targetVersion.GreaterThan(currentVersion) { + return fmt.Errorf("target version v%s is not greater than current version v%s", targetVersionS, currentVersionS) + } - scmd := internal.NewSpinnerCmd(migrater, "Upgrading") + wd, err := os.Getwd() + if err != nil { + return fmt.Errorf("cannot get current working directory: %v", err) + } - if err := scmd.Run(); err != nil && !skipSpinner { - cmd.Printf("fiber: failed to migrate: %v", err) - return + err = migrations.DoMigration(cmd, wd, currentVersion, targetVersion) + if err != nil { + return fmt.Errorf("migration failed %v", err) } - success := fmt.Sprintf("Done! Fiber cli is now at v%s!", cliLatestVersion) - cmd.Println(termenv.String(success).Foreground(termenv.ANSIBrightGreen)) + msg := fmt.Sprintf("Migration from Fiber %s to %s", currentVersionS, targetVersionS) + cmd.Println(termenv.String(msg). + Foreground(termenv.ANSIBrightBlue)) - migrated = true + return nil } diff --git a/cmd/version.go b/cmd/version.go index b94a107..1c0c93c 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -3,8 +3,9 @@ package cmd import ( "errors" "fmt" - "io/ioutil" + "io" "net/http" + "os" "regexp" "github.com/spf13/cobra" @@ -39,7 +40,7 @@ var currentVersionRegexp = regexp.MustCompile(`github\.com/gofiber/fiber[^\n]*? var currentVersionFile = "go.mod" func currentVersion() (string, error) { - b, err := ioutil.ReadFile(currentVersionFile) + b, err := os.ReadFile(currentVersionFile) if err != nil { return "", err } @@ -73,7 +74,7 @@ func latestVersion(isCli bool) (v string, err error) { _ = res.Body.Close() }() - if b, err = ioutil.ReadAll(res.Body); err != nil { + if b, err = io.ReadAll(res.Body); err != nil { return } diff --git a/development.md b/development.md index 08b2437..39895eb 100644 --- a/development.md +++ b/development.md @@ -1,13 +1,13 @@ # Local Development -Install [reflex](github.com/cespare/reflex) +Install [air](github.com/cosmtrek/air) ```bash -go install github.com/cespare/reflex@latest +go install github.com/cosmtrek/air@latest ``` -Use reflex to watch for changes in the project and recompile the binary +Use air to watch for changes in the project and recompile the binary ```bash -reflex -r '\.go$' -- go install ./fiber +air --build.cmd="go install ./fiber" ``` Test the binary in fiber project diff --git a/go.mod b/go.mod index 1b2adae..328f8e7 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/gofiber/cli go 1.21 require ( + github.com/Masterminds/semver/v3 v3.2.1 github.com/charmbracelet/bubbles v0.18.0 github.com/charmbracelet/bubbletea v0.25.0 github.com/containerd/console v1.0.4 diff --git a/go.sum b/go.sum index 76f7888..62f4861 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= +github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= From bc19709cff8ed0cb3a652f0df744a3ade60d6954 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9?= Date: Sun, 21 Apr 2024 21:30:44 +0200 Subject: [PATCH 04/27] add command for migration purpose --- cmd/internal/migrations/common.go | 2 +- cmd/internal/migrations/lists.go | 2 +- cmd/internal/migrations/v3/common.go | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/cmd/internal/migrations/common.go b/cmd/internal/migrations/common.go index da2dca4..74e041c 100644 --- a/cmd/internal/migrations/common.go +++ b/cmd/internal/migrations/common.go @@ -2,7 +2,7 @@ package migrations import ( "fmt" - "github.com/Masterminds/semver/v3" + semver "github.com/Masterminds/semver/v3" "github.com/spf13/cobra" "os" "regexp" diff --git a/cmd/internal/migrations/lists.go b/cmd/internal/migrations/lists.go index b988d3e..6029642 100644 --- a/cmd/internal/migrations/lists.go +++ b/cmd/internal/migrations/lists.go @@ -2,7 +2,7 @@ package migrations import ( "fmt" - "github.com/Masterminds/semver/v3" + semver "github.com/Masterminds/semver/v3" "github.com/spf13/cobra" "github.com/gofiber/cli/cmd/internal/migrations/v3" diff --git a/cmd/internal/migrations/v3/common.go b/cmd/internal/migrations/v3/common.go index d1d35bf..4a8fc42 100644 --- a/cmd/internal/migrations/v3/common.go +++ b/cmd/internal/migrations/v3/common.go @@ -2,7 +2,6 @@ package v3 import ( "fmt" - "github.com/Masterminds/semver/v3" "github.com/spf13/cobra" "strings" From 01832ab56d436bb630eec3067d10fa921291e1bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9?= Date: Sun, 1 Dec 2024 13:42:46 +0100 Subject: [PATCH 05/27] refresh --- .golangci.yml | 377 ++++++++++++++++++++++++++++++++++++++++++++++ .markdownlint.yml | 248 ++++++++++++++++++++++++++++++ Makefile | 65 ++++++++ go.mod | 35 ++--- go.sum | 71 ++++----- 5 files changed, 744 insertions(+), 52 deletions(-) create mode 100644 .golangci.yml create mode 100644 .markdownlint.yml create mode 100644 Makefile diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..8b8d27b --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,377 @@ +# v1.2.0. Created based on golangci-lint v1.57.1 + +run: + timeout: 5m + modules-download-mode: readonly + allow-serial-runners: true + +output: + sort-results: true + uniq-by-line: false + +linters-settings: + depguard: + rules: + all: + list-mode: lax + deny: + - pkg: "flag" + desc: '`flag` package is only allowed in main.go' + - pkg: "log" + desc: 'logging is provided by `pkg/log`' + - pkg: "io/ioutil" + desc: '`io/ioutil` package is deprecated, use the `io` and `os` package instead' + # TODO: Prevent using these without a reason + # - pkg: "reflect" + # desc: '`reflect` package is dangerous to use' + # - pkg: "unsafe" + # desc: '`unsafe` package is dangerous to use' + + errcheck: + check-type-assertions: true + check-blank: true + disable-default-exclusions: true + exclude-functions: + - '(*bytes.Buffer).Write' # always returns nil error + - '(*github.com/valyala/bytebufferpool.ByteBuffer).Write' # always returns nil error + - '(*github.com/valyala/bytebufferpool.ByteBuffer).WriteByte' # always returns nil error + - '(*github.com/valyala/bytebufferpool.ByteBuffer).WriteString' # always returns nil error + + errchkjson: + report-no-exported: true + + exhaustive: + check-generated: true + default-signifies-exhaustive: true + + forbidigo: + forbid: + - ^print(ln)?$ + - ^fmt\.Print(f|ln)?$ + - ^http\.Default(Client|ServeMux|Transport)$ + # TODO: Eventually enable these patterns + # - ^panic$ + # - ^time\.Sleep$ + analyze-types: true + + gci: + sections: + - standard + - prefix(github.com/gofiber/fiber) + - default + - blank + - dot + # - alias + custom-order: true + + goconst: + numbers: true + + gocritic: + # TODO: Uncomment the following lines + enabled-tags: + - diagnostic + # - style + # - performance + # - experimental + # - opinionated + settings: + captLocal: + paramsOnly: false + elseif: + skipBalanced: false + underef: + skipRecvDeref: false + # NOTE: Set this option to false if other projects rely on this project's code + # unnamedResult: + # checkExported: false + + gofumpt: + module-path: github.com/gofiber/fiber + extra-rules: true + + gosec: + excludes: + - G104 # TODO: Enable this again. Mostly provided by errcheck + config: + global: + # show-ignored: true # TODO: Enable this + audit: true + + govet: + enable-all: true + disable: + - shadow + + grouper: + # const-require-grouping: true # TODO: Enable this + import-require-single-import: true + import-require-grouping: true + # var-require-grouping: true # TODO: Conflicts with gofumpt + + loggercheck: + require-string-key: true + no-printf-like: true + + misspell: + locale: US + + nolintlint: + require-explanation: true + require-specific: true + + nonamedreturns: + report-error-in-defer: true + + perfsprint: + err-error: true + + predeclared: + q: true + + promlinter: + strict: true + + # TODO: Enable this + # reassign: + # patterns: + # - '.*' + + revive: + enable-all-rules: true + rules: + # Provided by gomnd linter + - name: add-constant + disabled: true + - name: argument-limit + disabled: true + # Provided by bidichk + - name: banned-characters + disabled: true + - name: cognitive-complexity + disabled: true + - name: comment-spacings + arguments: + - nolint + disabled: true # TODO: Do not disable + - name: cyclomatic + disabled: true + # TODO: Enable this check. Currently disabled due to upstream bug. + # - name: enforce-repeated-arg-type-style + # arguments: + # - short + - name: enforce-slice-style + arguments: + - make + disabled: true # TODO: Do not disable + - name: exported + disabled: true + - name: file-header + disabled: true + - name: function-result-limit + arguments: [3] + - name: function-length + disabled: true + - name: line-length-limit + disabled: true + - name: max-public-structs + disabled: true + - name: modifies-parameter + disabled: true + - name: nested-structs + disabled: true # TODO: Do not disable + - name: package-comments + disabled: true + - name: optimize-operands-order + disabled: true + - name: unchecked-type-assertion + disabled: true # TODO: Do not disable + - name: unhandled-error + arguments: ['bytes\.Buffer\.Write'] + + stylecheck: + checks: + - all + - -ST1000 + - -ST1020 + - -ST1021 + - -ST1022 + + tagalign: + strict: true + + tagliatelle: + case: + rules: + json: snake + + tenv: + all: true + + testifylint: + enable-all: true + + testpackage: + skip-regexp: "^$" + + unparam: + # NOTE: Set this option to false if other projects rely on this project's code + check-exported: false + + unused: + # TODO: Uncomment these two lines + # parameters-are-used: false + # local-variables-are-used: false + # NOTE: Set these options to true if other projects rely on this project's code + field-writes-are-uses: true + # exported-is-used: true # TODO: Fix issues with this option (upstream) + exported-fields-are-used: true + + usestdlibvars: + http-method: true + http-status-code: true + time-weekday: false # TODO: Set to true + time-month: false # TODO: Set to true + time-layout: false # TODO: Set to true + crypto-hash: true + default-rpc-path: true + sql-isolation-level: true + tls-signature-scheme: true + constant-kind: true + + wrapcheck: + ignorePackageGlobs: + - github.com/gofiber/fiber/* + - github.com/valyala/fasthttp + +issues: + exclude-use-default: false + exclude-case-sensitive: true + max-issues-per-linter: 0 + max-same-issues: 0 + exclude-dirs: + - internal # TODO: Do not ignore interal packages + exclude-rules: + - linters: + - err113 + text: 'do not define dynamic errors, use wrapped static errors instead*' + - path: log/.*\.go + linters: + - depguard + # Exclude some linters from running on tests files. + - path: _test\.go + linters: + - bodyclose + - err113 + # fix: true + +linters: + enable: + - asasalint + - asciicheck + - bidichk + - bodyclose + - containedctx + - contextcheck + # - cyclop + - decorder + - depguard + - dogsled + # - dupl + - dupword # TODO: Enable + - durationcheck + - errcheck + - errchkjson + - errname + - errorlint + - exhaustive + # - exhaustivestruct + # - exhaustruct + - copyloopvar + - forbidigo + - forcetypeassert + # - funlen + # - gci # TODO: Enable + - ginkgolinter + # - gocheckcompilerdirectives # TODO: Enable + # - gochecknoglobals # TODO: Enable + # - gochecknoinits # TODO: Enable + - gochecksumtype + # - gocognit + - goconst # TODO: Enable + - gocritic + # - gocyclo + # - godot + # - godox + - err113 + - gofmt + - gofumpt + # - goheader + - goimports + # - mnd # TODO: Enable + - gomoddirectives + # - gomodguard + - goprintffuncname + - gosec + - gosimple + # - gosmopolitan # TODO: Enable + - govet + - grouper + # - ifshort # TODO: Enable + # - importas + # - inamedparam + - ineffassign + # - interfacebloat + # - interfacer + # - ireturn + # - lll + - loggercheck + # - maintidx + - makezero + # - maligned + - mirror + - misspell + - musttag + - nakedret + # - nestif + - nilerr + - nilnil + # - nlreturn + - noctx + - nolintlint + - nonamedreturns + - nosprintfhostport + # - paralleltest # TODO: Enable + - perfsprint + # - prealloc + - predeclared + - promlinter + - protogetter + - reassign + - revive + - rowserrcheck + # - scopelint # TODO: Enable + - sloglint + - spancheck + - sqlclosecheck + - staticcheck + - stylecheck + # - tagalign # TODO: Enable + - tagliatelle + - tenv + - testableexamples + - testifylint + # - testpackage # TODO: Enable + - thelper + - tparallel + - typecheck + - unconvert + - unparam + - unused + - usestdlibvars + # - varnamelen + # - wastedassign # TODO: Enable + - whitespace + - wrapcheck + # - wsl + - zerologlint diff --git a/.markdownlint.yml b/.markdownlint.yml new file mode 100644 index 0000000..71d7b30 --- /dev/null +++ b/.markdownlint.yml @@ -0,0 +1,248 @@ +# Example markdownlint configuration with all properties set to their default value + +# Default state for all rules +default: true + +# Path to configuration file to extend +extends: null + +# MD001/heading-increment : Heading levels should only increment by one level at a time : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md001.md +MD001: true + +# MD003/heading-style : Heading style : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md003.md +MD003: + # Heading style + style: "consistent" + +# MD004/ul-style : Unordered list style : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md004.md +MD004: + # List style + style: "consistent" + +# MD005/list-indent : Inconsistent indentation for list items at the same level : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md005.md +MD005: true + +# MD007/ul-indent : Unordered list indentation : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md007.md +MD007: + # Spaces for indent + indent: + # Whether to indent the first level of the list + start_indented: false + # Spaces for first level indent (when start_indented is set) + start_indent: 2 + +# MD009/no-trailing-spaces : Trailing spaces : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md009.md +MD009: + # Spaces for line break + br_spaces: 2 + # Allow spaces for empty lines in list items + list_item_empty_lines: false + # Include unnecessary breaks + strict: true + +# MD010/no-hard-tabs : Hard tabs : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md010.md +MD010: + # Include code blocks + code_blocks: true + # Fenced code languages to ignore + ignore_code_languages: [] + # Number of spaces for each hard tab + spaces_per_tab: 4 + +# MD011/no-reversed-links : Reversed link syntax : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md011.md +MD011: true + +# MD012/no-multiple-blanks : Multiple consecutive blank lines : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md012.md +MD012: + # Consecutive blank lines + maximum: 1 + +# MD013/line-length : Line length : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md013.md +MD013: false + +# MD014/commands-show-output : Dollar signs used before commands without showing output : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md014.md +MD014: true + +# MD018/no-missing-space-atx : No space after hash on atx style heading : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md018.md +MD018: true + +# MD019/no-multiple-space-atx : Multiple spaces after hash on atx style heading : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md019.md +MD019: true + +# MD020/no-missing-space-closed-atx : No space inside hashes on closed atx style heading : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md020.md +MD020: true + +# MD021/no-multiple-space-closed-atx : Multiple spaces inside hashes on closed atx style heading : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md021.md +MD021: true + +# MD022/blanks-around-headings : Headings should be surrounded by blank lines : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md022.md +MD022: + # Blank lines above heading + lines_above: 1 + # Blank lines below heading + lines_below: 1 + +# MD023/heading-start-left : Headings must start at the beginning of the line : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md023.md +MD023: true + +# MD024/no-duplicate-heading : Multiple headings with the same content : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md024.md +MD024: false + +# MD025/single-title/single-h1 : Multiple top-level headings in the same document : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md025.md +MD025: + # Heading level + level: 1 + # RegExp for matching title in front matter + front_matter_title: "^\\s*title\\s*[:=]" + +# MD026/no-trailing-punctuation : Trailing punctuation in heading : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md026.md +MD026: + # Punctuation characters + punctuation: ".,;:!。,;:!" + +# MD027/no-multiple-space-blockquote : Multiple spaces after blockquote symbol : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md027.md +MD027: true + +# MD028/no-blanks-blockquote : Blank line inside blockquote : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md028.md +MD028: true + +# MD029/ol-prefix : Ordered list item prefix : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md029.md +MD029: + # List style + style: "one_or_ordered" + +# MD030/list-marker-space : Spaces after list markers : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md030.md +MD030: + # Spaces for single-line unordered list items + ul_single: 1 + # Spaces for single-line ordered list items + ol_single: 1 + # Spaces for multi-line unordered list items + ul_multi: 1 + # Spaces for multi-line ordered list items + ol_multi: 1 + +# MD031/blanks-around-fences : Fenced code blocks should be surrounded by blank lines : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md031.md +MD031: + # Include list items + list_items: true + +# MD032/blanks-around-lists : Lists should be surrounded by blank lines : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md032.md +MD032: true + +# MD033/no-inline-html : Inline HTML : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md033.md +MD033: false + +# MD034/no-bare-urls : Bare URL used : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md034.md +MD034: true + +# MD035/hr-style : Horizontal rule style : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md035.md +MD035: + # Horizontal rule style + style: "consistent" + +# MD036/no-emphasis-as-heading : Emphasis used instead of a heading : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md036.md +MD036: + # Punctuation characters + punctuation: ".,;:!?。,;:!?" + +# MD037/no-space-in-emphasis : Spaces inside emphasis markers : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md037.md +MD037: true + +# MD038/no-space-in-code : Spaces inside code span elements : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md038.md +MD038: true + +# MD039/no-space-in-links : Spaces inside link text : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md039.md +MD039: true + +# MD040/fenced-code-language : Fenced code blocks should have a language specified : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md040.md +MD040: + # List of languages + allowed_languages: [] + # Require language only + language_only: false + +# MD041/first-line-heading/first-line-h1 : First line in a file should be a top-level heading : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md041.md +MD041: + # Heading level + level: 1 + # RegExp for matching title in front matter + front_matter_title: "^\\s*title\\s*[:=]" + +# MD042/no-empty-links : No empty links : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md042.md +MD042: true + +# MD043/required-headings : Required heading structure : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md043.md +MD043: false + +# MD044/proper-names : Proper names should have the correct capitalization : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md044.md +MD044: + # List of proper names + names: [] + # Include code blocks + code_blocks: true + # Include HTML elements + html_elements: true + +# MD045/no-alt-text : Images should have alternate text (alt text) : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md045.md +MD045: false + +# MD046/code-block-style : Code block style : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md046.md +MD046: + # Block style + style: "fenced" + +# MD047/single-trailing-newline : Files should end with a single newline character : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md047.md +MD047: true + +# MD048/code-fence-style : Code fence style : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md048.md +MD048: + # Code fence style + style: "backtick" + +# MD049/emphasis-style : Emphasis style : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md049.md +MD049: + # Emphasis style + style: "consistent" + +# MD050/strong-style : Strong style : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md050.md +MD050: + # Strong style + style: "consistent" + +# MD051/link-fragments : Link fragments should be valid : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md051.md +MD051: true + +# MD052/reference-links-images : Reference links and images should use a label that is defined : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md052.md +MD052: + # Include shortcut syntax + shortcut_syntax: false + +# MD053/link-image-reference-definitions : Link and image reference definitions should be needed : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md053.md +MD053: + # Ignored definitions + ignored_definitions: + - "//" + +# MD054/link-image-style : Link and image style : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md054.md +MD054: + # Allow autolinks + autolink: false + # Allow inline links and images + inline: true + # Allow full reference links and images + full: true + # Allow collapsed reference links and images + collapsed: true + # Allow shortcut reference links and images + shortcut: true + # Allow URLs as inline links + url_inline: true + +# MD055/table-pipe-style : Table pipe style : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md055.md +MD055: + # Table pipe style + style: "consistent" + +# MD056/table-column-count : Table column count : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md056.md +MD056: true diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..41eb8f5 --- /dev/null +++ b/Makefile @@ -0,0 +1,65 @@ +## help: 💡 Display available commands +.PHONY: help +help: + @echo '⚡️ GoFiber/Cli Development:' + @sed -n 's/^##//p' ${MAKEFILE_LIST} | column -t -s ':' | sed -e 's/^/ /' + +## audit: 🚀 Conduct quality checks +.PHONY: audit +audit: + go mod verify + go vet ./... + go run golang.org/x/vuln/cmd/govulncheck@latest ./... + +## benchmark: 📈 Benchmark code performance +.PHONY: benchmark +benchmark: + go test ./... -benchmem -bench=. -run=^Benchmark_$ + +## coverage: ☂️ Generate coverage report +.PHONY: coverage +coverage: + go run gotest.tools/gotestsum@latest -f testname -- ./... -race -count=1 -coverprofile=/tmp/coverage.out -covermode=atomic + go tool cover -html=/tmp/coverage.out + +## format: 🎨 Fix code format issues +.PHONY: format +format: + go run mvdan.cc/gofumpt@latest -w -l . + +## markdown: 🎨 Find markdown format issues (Requires markdownlint-cli) +.PHONY: markdown +markdown: + markdownlint-cli2 "**/*.md" "#vendor" + +## lint: 🚨 Run lint checks +.PHONY: lint +lint: + go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.62.0 run ./... + +## test: 🚦 Execute all tests +.PHONY: test +test: + go run gotest.tools/gotestsum@latest -f testname -- ./... -race -count=1 -shuffle=on + +## longtest: 🚦 Execute all tests 10x +.PHONY: longtest +longtest: + go run gotest.tools/gotestsum@latest -f testname -- ./... -race -count=15 -shuffle=on + +## tidy: 📌 Clean and tidy dependencies +.PHONY: tidy +tidy: + go mod tidy -v + +## betteralign: 📐 Optimize alignment of fields in structs +.PHONY: betteralign +betteralign: + go run github.com/dkorunic/betteralign/cmd/betteralign@latest -test_files -generated_files -apply ./... + +## generate: ⚡️ Generate msgp && interface implementations +.PHONY: generate +generate: + go install github.com/tinylib/msgp@latest + go install github.com/vburenin/ifacemaker@975a95966976eeb2d4365a7fb236e274c54da64c + go generate ./... diff --git a/go.mod b/go.mod index 328f8e7..d84271d 100644 --- a/go.mod +++ b/go.mod @@ -1,41 +1,42 @@ module github.com/gofiber/cli -go 1.21 +go 1.22 require ( - github.com/Masterminds/semver/v3 v3.2.1 - github.com/charmbracelet/bubbles v0.18.0 - github.com/charmbracelet/bubbletea v0.25.0 + github.com/Masterminds/semver/v3 v3.3.1 + github.com/charmbracelet/bubbles v0.20.0 + github.com/charmbracelet/bubbletea v1.2.4 github.com/containerd/console v1.0.4 - github.com/fsnotify/fsnotify v1.7.0 + github.com/fsnotify/fsnotify v1.8.0 github.com/jarcoal/httpmock v1.3.1 github.com/muesli/termenv v0.15.2 - github.com/spf13/cobra v1.8.0 - github.com/stretchr/testify v1.9.0 + github.com/spf13/cobra v1.8.1 + github.com/stretchr/testify v1.10.0 ) require ( github.com/atotto/clipboard v0.1.4 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect - github.com/charmbracelet/lipgloss v0.9.1 // indirect + github.com/charmbracelet/lipgloss v1.0.0 // indirect + github.com/charmbracelet/x/ansi v0.5.2 // indirect + github.com/charmbracelet/x/term v0.2.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kr/text v0.2.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect - github.com/mattn/go-isatty v0.0.18 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-localereader v0.0.1 // indirect - github.com/mattn/go-runewidth v0.0.15 // indirect - github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect - github.com/muesli/reflow v0.3.0 // indirect github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/rivo/uniseg v0.4.6 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.12.0 // indirect - golang.org/x/term v0.6.0 // indirect - golang.org/x/text v0.3.8 // indirect + golang.org/x/sync v0.9.0 // indirect + golang.org/x/sys v0.27.0 // indirect + golang.org/x/text v0.20.0 // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 62f4861..8fb4e8f 100644 --- a/go.sum +++ b/go.sum @@ -1,23 +1,29 @@ -github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= -github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4= +github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= -github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0= -github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw= -github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM= -github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg= -github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg= -github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I= +github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE= +github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU= +github.com/charmbracelet/bubbletea v1.2.4 h1:KN8aCViA0eps9SCOThb2/XPIlea3ANJLUkv3KnQRNCE= +github.com/charmbracelet/bubbletea v1.2.4/go.mod h1:Qr6fVQw+wX7JkWWkVyXYk/ZUQ92a6XNekLXa3rR18MM= +github.com/charmbracelet/lipgloss v1.0.0 h1:O7VkGDvqEdGi93X+DeqsQ7PKHDgtQfF8j8/O2qFMQNg= +github.com/charmbracelet/lipgloss v1.0.0/go.mod h1:U5fy9Z+C38obMs+T+tJqst9VGzlOYGj4ri9reL3qUlo= +github.com/charmbracelet/x/ansi v0.5.2 h1:dEa1x2qdOZXD/6439s+wF7xjV+kZLu/iN00GuXXrU9E= +github.com/charmbracelet/x/ansi v0.5.2/go.mod h1:KBUFw1la39nl0dLl10l5ORDAqGXaeurTQmwyyVKse/Q= +github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= +github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro= github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= -github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= +github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww= @@ -28,48 +34,43 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= -github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= -github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g= github.com/maxatome/go-testdeep v1.12.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM= -github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34= -github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= -github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= -github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.6 h1:Sovz9sDSwbOz9tgUy8JpT+KgCkPYJEN/oYzlJiYTNLg= -github.com/rivo/uniseg v0.4.6/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= -github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= +golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From ca613cde8b14d5167b0bf601e5e05ba9db117f08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9?= Date: Sat, 12 Jul 2025 19:29:48 +0200 Subject: [PATCH 06/27] add migration scripts --- cmd/internal/migrations/lists.go | 11 +- cmd/internal/migrations/v3/common.go | 70 ++++++++++- cmd/internal/migrations/v3/common_test.go | 137 ++++++++++++++++++++++ 3 files changed, 216 insertions(+), 2 deletions(-) create mode 100644 cmd/internal/migrations/v3/common_test.go diff --git a/cmd/internal/migrations/lists.go b/cmd/internal/migrations/lists.go index 6029642..61ae73b 100644 --- a/cmd/internal/migrations/lists.go +++ b/cmd/internal/migrations/lists.go @@ -22,7 +22,16 @@ type Migration struct { // Example structure: // {"from": ">=2.0.0", "to": "<=3.*.*", "fn": [MigrateFN, MigrateFN]} var Migrations = []Migration{ - {From: ">=2.0.0", To: "<4.0.0-0", Functions: []MigrationFn{v3.MigrateHandlerSignatures}}, + { + From: ">=2.0.0", + To: "<4.0.0-0", + Functions: []MigrationFn{ + v3.MigrateHandlerSignatures, + v3.MigrateParserMethods, + v3.MigrateRedirectMethods, + v3.MigrateGenericHelpers, + }, + }, {From: ">=1.0.0", To: ">=0.0.0-0", Functions: []MigrationFn{MigrateGoPkgs}}, } diff --git a/cmd/internal/migrations/v3/common.go b/cmd/internal/migrations/v3/common.go index 4a8fc42..d66cd62 100644 --- a/cmd/internal/migrations/v3/common.go +++ b/cmd/internal/migrations/v3/common.go @@ -2,9 +2,12 @@ package v3 import ( "fmt" - "github.com/spf13/cobra" + "regexp" "strings" + semver "github.com/Masterminds/semver/v3" + "github.com/spf13/cobra" + "github.com/gofiber/cli/cmd/internal" ) @@ -22,3 +25,68 @@ func MigrateHandlerSignatures(cmd *cobra.Command, cwd string, _ *semver.Version, return nil } + +// MigrateParserMethods replaces deprecated parser helper methods with the new binding API +func MigrateParserMethods(cmd *cobra.Command, cwd string, _ *semver.Version, _ *semver.Version) error { + replacer := strings.NewReplacer( + ".BodyParser(", ".Bind().Body(", + ".CookieParser(", ".Bind().Cookie(", + ".ParamsParser(", ".Bind().URI(", + ".QueryParser(", ".Bind().Query(", + ) + + err := internal.ChangeFileContent(cwd, func(content string) string { + return replacer.Replace(content) + }) + if err != nil { + return fmt.Errorf("failed to migrate parser methods: %v", err) + } + + cmd.Println("Migrating parser methods") + return nil +} + +// MigrateRedirectMethods updates redirect helper methods to the new API +func MigrateRedirectMethods(cmd *cobra.Command, cwd string, _ *semver.Version, _ *semver.Version) error { + replacer := strings.NewReplacer( + ".RedirectBack(", ".Redirect().Back(", + ".RedirectToRoute(", ".Redirect().Route(", + ) + + err := internal.ChangeFileContent(cwd, func(content string) string { + re := regexp.MustCompile(`\.Redirect\(`) + content = re.ReplaceAllString(content, ".Redirect().To(") + return replacer.Replace(content) + }) + if err != nil { + return fmt.Errorf("failed to migrate redirect methods: %v", err) + } + + cmd.Println("Migrating redirect methods") + return nil +} + +// MigrateGenericHelpers migrates helper functions that now use generics +func MigrateGenericHelpers(cmd *cobra.Command, cwd string, _ *semver.Version, _ *semver.Version) error { + 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 + }) + if err != nil { + return fmt.Errorf("failed to migrate generic helpers: %v", err) + } + + cmd.Println("Migrating generic helpers") + return nil +} diff --git a/cmd/internal/migrations/v3/common_test.go b/cmd/internal/migrations/v3/common_test.go new file mode 100644 index 0000000..1a96e50 --- /dev/null +++ b/cmd/internal/migrations/v3/common_test.go @@ -0,0 +1,137 @@ +package v3 + +import ( + "bytes" + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/spf13/cobra" + "github.com/stretchr/testify/assert" +) + +func writeTempFile(t *testing.T, dir, name, content string) string { + t.Helper() + path := filepath.Join(dir, name) + err := os.WriteFile(path, []byte(content), 0o644) + assert.Nil(t, err) + return path +} + +func readFile(t *testing.T, path string) string { + t.Helper() + b, err := os.ReadFile(path) + assert.Nil(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_MigrateHandlerSignatures(t *testing.T) { + dir, err := ioutil.TempDir("", "mhstest") + assert.Nil(t, err) + defer os.RemoveAll(dir) + + file := writeTempFile(t, dir, "main.go", `package main +import "github.com/gofiber/fiber/v2" +func handler(c *fiber.Ctx) error { return nil } +`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + assert.Nil(t, MigrateHandlerSignatures(cmd, dir, nil, nil)) + + content := readFile(t, file) + assert.NotContains(t, content, "*fiber.Ctx") + assert.Contains(t, content, "fiber.Ctx") + assert.Contains(t, buf.String(), "Migrating handler signatures") +} + +func Test_MigrateParserMethods(t *testing.T) { + dir, err := ioutil.TempDir("", "mptest") + assert.Nil(t, err) + defer os.RemoveAll(dir) + + file := writeTempFile(t, dir, "main.go", `package main +import "github.com/gofiber/fiber/v2" +func handler(c fiber.Ctx) error { + var v interface{} + c.BodyParser(&v) + c.CookieParser(&v) + c.ParamsParser(&v) + c.QueryParser(&v) + return nil +} +`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + assert.Nil(t, MigrateParserMethods(cmd, dir, nil, nil)) + + content := readFile(t, file) + assert.Contains(t, content, ".Bind().Body(&v)") + assert.Contains(t, content, ".Bind().Cookie(&v)") + assert.Contains(t, content, ".Bind().URI(&v)") + assert.Contains(t, content, ".Bind().Query(&v)") + assert.Contains(t, buf.String(), "Migrating parser methods") +} + +func Test_MigrateRedirectMethods(t *testing.T) { + dir, err := ioutil.TempDir("", "mrtest") + assert.Nil(t, err) + defer os.RemoveAll(dir) + + file := writeTempFile(t, dir, "main.go", `package main +import "github.com/gofiber/fiber/v2" +func handler(c fiber.Ctx) error { + c.Redirect("/foo") + c.RedirectBack() + c.RedirectToRoute("home") + return nil +} +`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + assert.Nil(t, MigrateRedirectMethods(cmd, dir, nil, nil)) + + content := readFile(t, file) + assert.Contains(t, content, ".Redirect().To(\"/foo\")") + assert.Contains(t, content, ".Redirect().Back()") + assert.Contains(t, content, ".Redirect().Route(\"home\")") + assert.Contains(t, buf.String(), "Migrating redirect methods") +} + +func Test_MigrateGenericHelpers(t *testing.T) { + dir, err := ioutil.TempDir("", "mghtest") + assert.Nil(t, err) + defer os.RemoveAll(dir) + + file := writeTempFile(t, dir, "main.go", `package main +import "github.com/gofiber/fiber/v2" +func handler(c fiber.Ctx) error { + _ = c.ParamsInt("id", 0) + _ = c.QueryInt("age", 0) + _ = c.QueryFloat("score", 0.5) + _ = c.QueryBool("ok", true) + return nil +} +`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + assert.Nil(t, MigrateGenericHelpers(cmd, dir, nil, nil)) + + content := readFile(t, file) + assert.Contains(t, content, "fiber.Params[int](c, \"id\"") + assert.Contains(t, content, "fiber.Query[int](c, \"age\"") + assert.Contains(t, content, "fiber.Query[float64](c, \"score\"") + assert.Contains(t, content, "fiber.Query[bool](c, \"ok\"") + assert.Contains(t, buf.String(), "Migrating generic helpers") +} From a3f14413695e62ca4d2ccba2f59ea9746e2c2fc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9?= Date: Sat, 12 Jul 2025 19:33:55 +0200 Subject: [PATCH 07/27] update workflows --- .github/release-drafter.yml | 69 ++++++++++++++++++++------------- .github/workflows/linter.yml | 55 +++++++++++++++++++------- .github/workflows/markdown.yml | 22 +++++++++++ .github/workflows/security.yml | 12 ------ .github/workflows/vulncheck.yml | 34 ++++++++++++++++ .golangci.yml | 16 +++++--- 6 files changed, 148 insertions(+), 60 deletions(-) create mode 100644 .github/workflows/markdown.yml delete mode 100644 .github/workflows/security.yml create mode 100644 .github/workflows/vulncheck.yml diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 6df7489..041e8cf 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -1,37 +1,50 @@ name-template: 'v$RESOLVED_VERSION' tag-template: 'v$RESOLVED_VERSION' categories: - - title: '🚀 New' - labels: - - '✏️ Feature' - - title: '🧹 Updates' - labels: - - '🧹 Updates' - - '🤖 Dependencies' - - title: '🐛 Fixes' - labels: - - '☢️ Bug' - - title: '📚 Documentation' - labels: - - '📒 Documentation' + - title: '❗ Breaking Changes' + labels: + - '❗ BreakingChange' + - title: '🚀 New' + labels: + - '✏️ Feature' + - '📝 Proposal' + - title: '🧹 Updates' + labels: + - '🧹 Updates' + - '⚡️ Performance' + - title: '🐛 Fixes' + labels: + - '☢️ Bug' + - title: '🛠️ Maintenance' + labels: + - '🤖 Dependencies' + - title: '📚 Documentation' + labels: + - '📒 Documentation' change-template: '- $TITLE (#$NUMBER)' change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. +exclude-contributors: + - dependabot + - dependabot[bot] version-resolver: - major: - labels: - - 'major' - minor: - labels: - - 'minor' - - '✏️ Feature' - patch: - labels: - - 'patch' - - '📒 Documentation' - - '☢️ Bug' - - '🤖 Dependencies' - - '🧹 Updates' - default: patch + major: + labels: + - 'major' + - '❗ BreakingChange' + minor: + labels: + - 'minor' + - '✏️ Feature' + - '📝 Proposal' + patch: + labels: + - 'patch' + - '📒 Documentation' + - '☢️ Bug' + - '🤖 Dependencies' + - '🧹 Updates' + - '⚡️ Performance' + default: patch autolabeler: - label: '📒 Documentation' title: diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 6ca66a2..76d68a3 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -1,16 +1,41 @@ -on: [push, pull_request] -name: Linter +name: golangci-lint + +on: + push: + branches: + - master + - main + paths-ignore: + - "**/*.md" + pull_request: + paths-ignore: + - "**/*.md" + +permissions: + # Required: allow read access to the content for analysis. + contents: read + # Optional: allow read access to pull request. Use with `only-new-issues` option. + pull-requests: read + # Optional: Allow write access to checks to allow the action to annotate code in the PR. + checks: write + jobs: - golint: - runs-on: ubuntu-latest - steps: - - name: Check out repository - uses: actions/checkout@v4 - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version: 1.24.x - - name: Run Golint - uses: reviewdog/action-golangci-lint@v2 - with: - golangci_lint_flags: "--tests=false" + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v5 + with: + # NOTE: Keep this in sync with the version from go.mod + go-version: "1.24.x" + cache: false + + - name: golangci-lint + uses: golangci/golangci-lint-action@v6 + with: + # NOTE: Keep this in sync with the version from .golangci.yml + version: v1.64.7 + # NOTE(ldez): temporary workaround + install-mode: goinstall diff --git a/.github/workflows/markdown.yml b/.github/workflows/markdown.yml new file mode 100644 index 0000000..4358bc8 --- /dev/null +++ b/.github/workflows/markdown.yml @@ -0,0 +1,22 @@ +name: markdownlint + +on: + push: + branches: + - master + - main + pull_request: + +jobs: + markdownlint: + runs-on: ubuntu-latest + steps: + - name: Fetch Repository + uses: actions/checkout@v4 + + - name: Run markdownlint-cli2 + uses: DavidAnson/markdownlint-cli2-action@v20 + with: + globs: | + **/*.md + #vendor diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml deleted file mode 100644 index 3bb1c3e..0000000 --- a/.github/workflows/security.yml +++ /dev/null @@ -1,12 +0,0 @@ -on: [push, pull_request] -name: Security -jobs: - Gosec: - runs-on: ubuntu-latest - steps: - - name: Check out repository - uses: actions/checkout@v4 - - name: Run Gosec - uses: securego/gosec@v2 - with: args: -exclude-dir=internal/*/ ./... - diff --git a/.github/workflows/vulncheck.yml b/.github/workflows/vulncheck.yml new file mode 100644 index 0000000..31c252e --- /dev/null +++ b/.github/workflows/vulncheck.yml @@ -0,0 +1,34 @@ +name: Run govulncheck + +on: + push: + branches: + - master + - main + paths-ignore: + - "**/*.md" + pull_request: + paths-ignore: + - "**/*.md" + +jobs: + govulncheck-check: + runs-on: ubuntu-latest + env: + GO111MODULE: on + steps: + - name: Fetch Repository + uses: actions/checkout@v4 + + - name: Install Go + uses: actions/setup-go@v5 + with: + go-version: "stable" + check-latest: true + cache: false + + - name: Install Govulncheck + run: go install golang.org/x/vuln/cmd/govulncheck@latest + + - name: Run Govulncheck + run: govulncheck ./... diff --git a/.golangci.yml b/.golangci.yml index 8b8d27b..bbf51f0 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -7,7 +7,6 @@ run: output: sort-results: true - uniq-by-line: false linters-settings: depguard: @@ -150,6 +149,8 @@ linters-settings: disabled: true - name: cognitive-complexity disabled: true + - name: confusing-results + disabled: true - name: comment-spacings arguments: - nolint @@ -187,7 +188,7 @@ linters-settings: - name: unchecked-type-assertion disabled: true # TODO: Do not disable - name: unhandled-error - arguments: ['bytes\.Buffer\.Write'] + disabled: true stylecheck: checks: @@ -250,7 +251,10 @@ issues: max-issues-per-linter: 0 max-same-issues: 0 exclude-dirs: - - internal # TODO: Do not ignore interal packages + - internal # TODO: Do not ignore internal packages + exclude-files: + - '_msgp\.go' + - '_msgp_test\.go' exclude-rules: - linters: - err113 @@ -263,7 +267,10 @@ issues: linters: - bodyclose - err113 - # fix: true + - source: 'fmt.Fprintf?' + linters: + - errcheck + - revive linters: enable: @@ -358,7 +365,6 @@ linters: - stylecheck # - tagalign # TODO: Enable - tagliatelle - - tenv - testableexamples - testifylint # - testpackage # TODO: Enable From 245fdd9820b4dcc8fd58774e299afdc1cc2356ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9?= Date: Sat, 12 Jul 2025 19:34:39 +0200 Subject: [PATCH 08/27] update gitignore --- .gitignore | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 070eb2c..120a182 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,31 @@ +# Binaries for programs and plugins *.exe *.exe~ *.dll *.so *.dylib + +# Test binary, built with `go test -c` *.test -*.out -.idea/ +*.tmp + +# Output of the go coverage tool +**/*.out + +# IDE files +.vscode +.DS_Store +.idea + +# Misc +*.test.gz +*.test.zst +*.test.br +*.pprof +*.workspace + +# Dependencies +/vendor/ +vendor/ vendor +/Godeps/ From 29ad73c0530c5771dacb3e00d3be502fc692a0bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9?= Date: Sat, 12 Jul 2025 19:52:11 +0200 Subject: [PATCH 09/27] fix markdown errors --- .github/CODE_OF_CONDUCT.md | 5 ++-- .github/CONTRIBUTING.md | 4 ++- .github/ISSUE_TEMPLATE/---bug.md | 2 +- .github/ISSUE_TEMPLATE/---feature.md | 8 +++--- .github/ISSUE_TEMPLATE/---question.md | 2 +- .github/pull_request_template.md | 8 ++++-- README.md | 41 ++++++++++++++++----------- development.md | 7 +++-- 8 files changed, 46 insertions(+), 31 deletions(-) diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md index c8d7ee4..3b6379d 100644 --- a/.github/CODE_OF_CONDUCT.md +++ b/.github/CODE_OF_CONDUCT.md @@ -71,11 +71,12 @@ Community leaders will follow these Community Impact Guidelines in determining t ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, -available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. +available at [https://www.contributor-covenant.org/version/2/0/code_of_conduct.html](https://www.contributor-covenant.org/version/2/0/code_of_conduct.html). Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see the FAQ at -https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. +[FAQ](https://www.contributor-covenant.org/faq). Translations are available at +[https://www.contributor-covenant.org/translations](https://www.contributor-covenant.org/translations). diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 27e25db..1a32fbf 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -5,9 +5,11 @@ When contributing to this repository, please first discuss the change you wish t Please note: we have a [code of conduct](https://github.com/gofiber/fiber-cli/blob/master/.github/CODE_OF_CONDUCT.md), please follow it in all your interactions with the `Fiber` project. ## Pull Requests or Comits + Titles always we must use prefix according to below: > 🔥 Feature, ♻️ Refactor, 🩹 Fix, 🚨 Test, 📚 Doc, 🎨 Style + - 🔥 Feature: Add flow to add person - ♻️ Refactor: Rename file X to Y - 🩹 Fix: Improve flow @@ -17,7 +19,7 @@ Titles always we must use prefix according to below: All pull request that contains a feature or fix is mandatory to have unit tests. Your PR is only to be merged if you respect this flow. -# 👍 Contribute +## 👍 Contribute If you want to say **thank you** and/or support the active development of `Fiber`: diff --git a/.github/ISSUE_TEMPLATE/---bug.md b/.github/ISSUE_TEMPLATE/---bug.md index 262629f..9f90984 100644 --- a/.github/ISSUE_TEMPLATE/---bug.md +++ b/.github/ISSUE_TEMPLATE/---bug.md @@ -7,4 +7,4 @@ assignees: '' --- -**Issue description** +## Issue description diff --git a/.github/ISSUE_TEMPLATE/---feature.md b/.github/ISSUE_TEMPLATE/---feature.md index c3baf50..26cadbc 100644 --- a/.github/ISSUE_TEMPLATE/---feature.md +++ b/.github/ISSUE_TEMPLATE/---feature.md @@ -7,10 +7,10 @@ assignees: '' --- -**Is your feature request related to a problem?** +## Is your feature request related to a problem? -**Describe the solution you'd like** +## Describe the solution you'd like -**Describe alternatives you've considered** +## Describe alternatives you've considered -**Additional context** +## Additional context diff --git a/.github/ISSUE_TEMPLATE/---question.md b/.github/ISSUE_TEMPLATE/---question.md index 690645d..65adfea 100644 --- a/.github/ISSUE_TEMPLATE/---question.md +++ b/.github/ISSUE_TEMPLATE/---question.md @@ -7,4 +7,4 @@ assignees: '' --- -**Question description** +## Question description diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 6dcf949..271ec56 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,4 +1,6 @@ -**Please provide enough information so that others can review your pull request:** +# Pull Request Guide + +Please provide enough information so that others can review your pull request: @@ -6,6 +8,6 @@ -**Commit formatting** +## Commit formatting -Use emojis on commit messages so it provides an easy way of identifying the purpose or intention of a commit. Check out the emoji cheatsheet here: https://gitmoji.carloscuesta.me/ \ No newline at end of file +Use emojis on commit messages so it provides an easy way of identifying the purpose or intention of a commit. Check out the emoji cheatsheet here: [https://gitmoji.carloscuesta.me](https://gitmoji.carloscuesta.me) diff --git a/README.md b/README.md index 8e2d085..b98541e 100644 --- a/README.md +++ b/README.md @@ -1,51 +1,55 @@ # cli + Fiber Command Line Interface [![Packaging status](https://repology.org/badge/vertical-allrepos/fiber-cli.svg)](https://repology.org/project/fiber-cli/versions) -# Installation +## Installation + Requires Go 1.24 or later. ```bash go install github.com/gofiber/cli/fiber@latest ``` -# Commands +## Commands + ## fiber + ### Synopsis 🚀 Fiber is an Express inspired web framework written in Go with 💖 - -Learn more on https://gofiber.io - + +Learn more on [gofiber.io](https://gofiber.io) + CLI version v0.0.x ### Options -``` +```text -h, --help help for fiber ``` ## fiber dev + ### Synopsis Rerun the fiber project if watched files changed -``` +```bash fiber dev [flags] ``` - ### Examples -``` +```bash fiber dev --pre-run="command1 flag,command2 flag" Pre run specific commands before running the project ``` ### Options -``` +```text -a, --args strings arguments for exec -d, --delay duration delay to trigger rerun (default 1s) -D, --exclude_dirs strings ignore these directories (default [assets,tmp,vendor,node_modules]) @@ -58,17 +62,18 @@ fiber dev [flags] ``` ## fiber new + ### Synopsis Generate a new fiber project -``` +```bash fiber new PROJECT [module name] [flags] ``` ### Examples -``` +```bash fiber new fiber-demo Generates a project with go module name fiber-demo @@ -90,38 +95,40 @@ fiber new PROJECT [module name] [flags] ### Options -``` +```text -h, --help help for new -r, --repo string complex boilerplate repo name in github or other repo url (default "gofiber/boilerplate") -t, --template string basic|complex (default "basic") ``` ## fiber upgrade + ### Synopsis Upgrade Fiber cli if a newer version is available -``` +```bash fiber upgrade [flags] ``` ### Options -``` +```text -h, --help help for upgrade ``` ## fiber version + ### Synopsis Print the local and released version number of fiber -``` +```bash fiber version [flags] ``` ### Options -``` +```text -h, --help help for version ``` diff --git a/development.md b/development.md index 39895eb..44c3c92 100644 --- a/development.md +++ b/development.md @@ -1,17 +1,20 @@ # Local Development -Install [air](github.com/cosmtrek/air) +Install [air](https://github.com/cosmtrek/air) + ```bash go install github.com/cosmtrek/air@latest ``` Use air to watch for changes in the project and recompile the binary + ```bash air --build.cmd="go install ./fiber" ``` Test the binary in fiber project + ```bash cd my-fiber-project fiber version -``` \ No newline at end of file +``` From ed8115c33d3ddbcae22b4bd04092615b6a887a80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9?= Date: Sat, 12 Jul 2025 19:55:58 +0200 Subject: [PATCH 10/27] fix alignment with better aligment tool --- cmd/dev.go | 24 +++++++++++++----------- cmd/internal/cmd.go | 20 ++++++++++---------- cmd/internal/prompt.go | 4 ++-- cmd/internal/task.go | 6 +++--- 4 files changed, 28 insertions(+), 26 deletions(-) diff --git a/cmd/dev.go b/cmd/dev.go index cdde950..b44a1fc 100644 --- a/cmd/dev.go +++ b/cmd/dev.go @@ -62,15 +62,17 @@ type config struct { extensions []string excludeDirs []string excludeFiles []string - delay time.Duration preRun []string args []string + delay time.Duration } type escort struct { - config + ctx context.Context + stdoutPipe io.ReadCloser + stderrPipe io.ReadCloser + compiling atomic.Value - ctx context.Context terminate context.CancelFunc w *fsnotify.Watcher @@ -78,17 +80,17 @@ type escort struct { watcherErrors chan error sig chan os.Signal - wg sync.WaitGroup + bin *exec.Cmd + hitCh chan struct{} + hitFunc func() - binPath string - bin *exec.Cmd - stdoutPipe io.ReadCloser - stderrPipe io.ReadCloser - hitCh chan struct{} - hitFunc func() - compiling atomic.Value + binPath string preRunCommands [][]string + + config + + wg sync.WaitGroup } func newEscort(c config) *escort { diff --git a/cmd/internal/cmd.go b/cmd/internal/cmd.go index 75aac87..5a10aec 100644 --- a/cmd/internal/cmd.go +++ b/cmd/internal/cmd.go @@ -14,18 +14,18 @@ import ( ) type SpinnerCmd struct { - p *tea.Program + err error + p *tea.Program + cmd *exec.Cmd + + stdout chan []byte + stderr chan []byte + errCh chan error + title string + buf []byte spinnerModel spinner.Model size console.WinSize - err error - title string - cmd *exec.Cmd - - stdout chan []byte - stderr chan []byte - errCh chan error - buf []byte - done bool + done bool } func NewSpinnerCmd(cmd *exec.Cmd, title ...string) *SpinnerCmd { diff --git a/cmd/internal/prompt.go b/cmd/internal/prompt.go index dd32985..6d5ae8a 100644 --- a/cmd/internal/prompt.go +++ b/cmd/internal/prompt.go @@ -12,11 +12,11 @@ import ( type errMsg error type Prompt struct { - p *tea.Program - textInput input.Model err error + p *tea.Program title string answer string + textInput input.Model } func NewPrompt(title string, placeholder ...string) *Prompt { diff --git a/cmd/internal/task.go b/cmd/internal/task.go index 6f3b4b4..665bcd4 100644 --- a/cmd/internal/task.go +++ b/cmd/internal/task.go @@ -11,11 +11,11 @@ import ( type Task func() error type SpinnerTask struct { - p *tea.Program - spinnerModel spinner.Model err error - title string + p *tea.Program task Task + title string + spinnerModel spinner.Model } func NewSpinnerTask(title string, task Task) *SpinnerTask { From f30eebac8ce462f8d2f0008289437c69e0a1baf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9?= Date: Sat, 12 Jul 2025 19:57:45 +0200 Subject: [PATCH 11/27] fix var naming --- cmd/version_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd/version_test.go b/cmd/version_test.go index 05271e9..4a8f6ba 100644 --- a/cmd/version_test.go +++ b/cmd/version_test.go @@ -17,7 +17,7 @@ func Test_Version_Printer(t *testing.T) { httpmock.Activate() defer httpmock.DeactivateAndReset() - httpmock.RegisterResponder(http.MethodGet, latestVersionUrl, httpmock.NewBytesResponder(200, fakeVersionResponse)) + httpmock.RegisterResponder(http.MethodGet, latestVersionURL, httpmock.NewBytesResponder(200, fakeVersionResponse)) out, err := runCobraCmd(versionCmd) at.Nil(err) @@ -28,7 +28,7 @@ func Test_Version_Printer(t *testing.T) { httpmock.Activate() defer httpmock.DeactivateAndReset() - httpmock.RegisterResponder(http.MethodGet, latestVersionUrl, httpmock.NewBytesResponder(200, []byte("no version"))) + httpmock.RegisterResponder(http.MethodGet, latestVersionURL, httpmock.NewBytesResponder(200, []byte("no version"))) out, err := runCobraCmd(versionCmd) at.Nil(err) @@ -111,7 +111,7 @@ func Test_Version_Latest(t *testing.T) { httpmock.Activate() defer httpmock.DeactivateAndReset() - httpmock.RegisterResponder(http.MethodGet, latestVersionUrl, httpmock.NewErrorResponder(errors.New("network error"))) + httpmock.RegisterResponder(http.MethodGet, latestVersionURL, httpmock.NewErrorResponder(errors.New("network error"))) _, err := latestVersion(false) at.NotNil(err) @@ -121,7 +121,7 @@ func Test_Version_Latest(t *testing.T) { httpmock.Activate() defer httpmock.DeactivateAndReset() - httpmock.RegisterResponder(http.MethodGet, latestVersionUrl, httpmock.NewBytesResponder(200, fakeVersionResponse)) + httpmock.RegisterResponder(http.MethodGet, latestVersionURL, httpmock.NewBytesResponder(200, fakeVersionResponse)) v, err := latestVersion(false) at.Nil(err) @@ -132,13 +132,13 @@ func Test_Version_Latest(t *testing.T) { httpmock.Activate() defer httpmock.DeactivateAndReset() - httpmock.RegisterResponder(http.MethodGet, latestVersionUrl, httpmock.NewBytesResponder(200, []byte("no version"))) + httpmock.RegisterResponder(http.MethodGet, latestVersionURL, httpmock.NewBytesResponder(200, []byte("no version"))) _, err := latestVersion(false) at.NotNil(err) }) } -var latestVersionUrl = "https://api.github.com/repos/gofiber/fiber/releases/latest" +var latestVersionURL = "https://api.github.com/repos/gofiber/fiber/releases/latest" var fakeVersionResponse = []byte(`{ "url": "https://api.github.com/repos/gofiber/fiber/releases/32189569", "assets_url": "https://api.github.com/repos/gofiber/fiber/releases/32189569/assets", "upload_url": "https://uploads.github.com/repos/gofiber/fiber/releases/32189569/assets{?name,label}", "html_url": "https://github.com/gofiber/fiber/releases/tag/v2.0.6", "id": 32189569, "node_id": "MDc6UmVsZWFzZTMyMTg5NTY5", "tag_name": "v2.0.6", "target_commitish": "master", "name": "v2.0.6", "draft": false, "author": { "login": "Fenny", "id": 25108519, "node_id": "MDQ6VXNlcjI1MTA4NTE5", "avatar_url": "https://avatars1.githubusercontent.com/u/25108519?v=4", "gravatar_id": "", "url": "https://api.github.com/users/Fenny", "html_url": "https://github.com/Fenny", "followers_url": "https://api.github.com/users/Fenny/followers", "following_url": "https://api.github.com/users/Fenny/following{/other_user}", "gists_url": "https://api.github.com/users/Fenny/gists{/gist_id}", "starred_url": "https://api.github.com/users/Fenny/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/Fenny/subscriptions", "organizations_url": "https://api.github.com/users/Fenny/orgs", "repos_url": "https://api.github.com/users/Fenny/repos", "events_url": "https://api.github.com/users/Fenny/events{/privacy}", "received_events_url": "https://api.github.com/users/Fenny/received_events", "type": "User", "site_admin": false }, "prerelease": false, "created_at": "2020-10-05T19:54:02Z", "published_at": "2020-10-05T22:10:27Z", "assets": [], "tarball_url": "https://api.github.com/repos/gofiber/fiber/tarball/v2.0.6", "zipball_url": "https://api.github.com/repos/gofiber/fiber/zipball/v2.0.6" }`) From 31629fc40e4521f42518846c84005833868bd495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9?= Date: Sat, 12 Jul 2025 20:12:33 +0200 Subject: [PATCH 12/27] fix lint issues --- cmd/dev.go | 38 ++++++++++++----------- cmd/dev_test.go | 3 ++ cmd/helpers_test.go | 9 ++++++ cmd/internal/migrations/v3/common_test.go | 8 +++++ cmd/tester_test.go | 1 + cmd/upgrade_test.go | 2 ++ cmd/version.go | 8 +++-- cmd/version_test.go | 37 +++++++++++++++------- fiber/main.go | 4 ++- 9 files changed, 76 insertions(+), 34 deletions(-) diff --git a/cmd/dev.go b/cmd/dev.go index b44a1fc..34d1d2a 100644 --- a/cmd/dev.go +++ b/cmd/dev.go @@ -5,7 +5,6 @@ import ( "context" "fmt" "io" - "io/ioutil" "log" "os" "os/exec" @@ -101,16 +100,20 @@ func newEscort(c config) *escort { } } -func (e *escort) run() (err error) { - if err = e.init(); err != nil { - return +func (e *escort) run() error { + if err := e.init(); err != nil { + return err } log.Println("Welcome to fiber dev 👋") defer func() { - _ = e.w.Close() - _ = os.Remove(e.binPath) + if err := e.w.Close(); err != nil { + log.Printf("Failed to close watcher: %v", err) + } + if err := os.Remove(e.binPath); err != nil { + log.Printf("Failed to remove bin: %v", err) + } }() e.wg.Add(3) @@ -130,9 +133,10 @@ func (e *escort) run() (err error) { return nil } -func (e *escort) init() (err error) { +func (e *escort) init() error { + var err error if e.w, err = fsnotify.NewWatcher(); err != nil { - return + return err } e.watcherEvents = e.w.Events @@ -142,19 +146,17 @@ func (e *escort) init() (err error) { // normalize root if e.root, err = filepath.Abs(e.root); err != nil { - return + return err } // create bin target - var f *os.File - if f, err = ioutil.TempFile("", ""); err != nil { - return + f, err := os.CreateTemp("", "") + if err != nil { + return err + } + if cerr := f.Close(); cerr != nil { + return cerr } - defer func() { - if e := f.Close(); e != nil { - err = e - } - }() e.binPath = f.Name() if runtime.GOOS == "windows" { @@ -169,7 +171,7 @@ func (e *escort) init() (err error) { e.preRunCommands = parsePreRunCommands(c.preRun) - return + return nil } func (e *escort) watchingFiles() { diff --git a/cmd/dev_test.go b/cmd/dev_test.go index 44d2ac0..4f0efa3 100644 --- a/cmd/dev_test.go +++ b/cmd/dev_test.go @@ -247,6 +247,7 @@ func Test_Dev_IsRemoved(t *testing.T) { for _, tc := range cases { t.Run(tc.Op.String(), func(t *testing.T) { + t.Parallel() assert.Equal(t, tc.bool, isRemoved(tc.Op)) }) } @@ -268,6 +269,7 @@ func Test_Dev_IsCreated(t *testing.T) { for _, tc := range cases { t.Run(tc.Op.String(), func(t *testing.T) { + t.Parallel() assert.Equal(t, tc.bool, isCreated(tc.Op)) }) } @@ -289,6 +291,7 @@ func Test_Dev_IsChmoded(t *testing.T) { for _, tc := range cases { t.Run(tc.Op.String(), func(t *testing.T) { + t.Parallel() assert.Equal(t, tc.bool, isChmoded(tc.Op)) }) } diff --git a/cmd/helpers_test.go b/cmd/helpers_test.go index 6d4f858..853fbfd 100644 --- a/cmd/helpers_test.go +++ b/cmd/helpers_test.go @@ -28,12 +28,15 @@ func Test_Helpers_FormatLatency(t *testing.T) { for _, tc := range cases { t.Run(tc.d.String(), func(t *testing.T) { + t.Parallel() assert.Equal(t, formatLatency(tc.d), tc.expected) }) } } func Test_Helper_Replace(t *testing.T) { + t.Parallel() + at := assert.New(t) dir, err := ioutil.TempDir("", "test_helper_replace") @@ -50,13 +53,16 @@ func Test_Helper_Replace(t *testing.T) { } func Test_Helper_LoadConfig(t *testing.T) { + t.Parallel() at := assert.New(t) t.Run("no config file", func(t *testing.T) { + t.Parallel() at.Nil(loadConfig()) }) t.Run("has config file", func(t *testing.T) { + t.Parallel() origHome := homeDir tempHome := setupHomeDir(t, "LoadConfig") homeDir = tempHome @@ -78,10 +84,13 @@ func Test_Helper_LoadConfig(t *testing.T) { } func Test_Helper_StoreJson(t *testing.T) { + t.Parallel() + assert.NotNil(t, storeJson("", complex(1, 1))) } func Test_Helper_ConfigFilePath(t *testing.T) { + t.Parallel() dir := homeDir homeDir = "" assert.Equal(t, configName, configFilePath()) diff --git a/cmd/internal/migrations/v3/common_test.go b/cmd/internal/migrations/v3/common_test.go index 1a96e50..f07213e 100644 --- a/cmd/internal/migrations/v3/common_test.go +++ b/cmd/internal/migrations/v3/common_test.go @@ -34,6 +34,8 @@ func newCmd(buf *bytes.Buffer) *cobra.Command { } func Test_MigrateHandlerSignatures(t *testing.T) { + t.Parallel() + dir, err := ioutil.TempDir("", "mhstest") assert.Nil(t, err) defer os.RemoveAll(dir) @@ -54,6 +56,8 @@ func handler(c *fiber.Ctx) error { return nil } } func Test_MigrateParserMethods(t *testing.T) { + t.Parallel() + dir, err := ioutil.TempDir("", "mptest") assert.Nil(t, err) defer os.RemoveAll(dir) @@ -83,6 +87,8 @@ func handler(c fiber.Ctx) error { } func Test_MigrateRedirectMethods(t *testing.T) { + t.Parallel() + dir, err := ioutil.TempDir("", "mrtest") assert.Nil(t, err) defer os.RemoveAll(dir) @@ -109,6 +115,8 @@ func handler(c fiber.Ctx) error { } func Test_MigrateGenericHelpers(t *testing.T) { + t.Parallel() + dir, err := ioutil.TempDir("", "mghtest") assert.Nil(t, err) defer os.RemoveAll(dir) diff --git a/cmd/tester_test.go b/cmd/tester_test.go index 5686831..32b387e 100644 --- a/cmd/tester_test.go +++ b/cmd/tester_test.go @@ -31,6 +31,7 @@ func fakeExecCommand(command string, args ...string) *exec.Cmd { } func TestHelperProcess(t *testing.T) { + t.Helper() if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" { return } diff --git a/cmd/upgrade_test.go b/cmd/upgrade_test.go index 2b8d9cc..90c10f8 100644 --- a/cmd/upgrade_test.go +++ b/cmd/upgrade_test.go @@ -11,6 +11,7 @@ import ( ) func Test_Upgrade_upgradeRunE(t *testing.T) { + t.Parallel() at := assert.New(t) b := &bytes.Buffer{} @@ -43,6 +44,7 @@ func Test_Upgrade_upgradeRunE(t *testing.T) { } func Test_Upgrade_upgrade(t *testing.T) { + t.Parallel() at := assert.New(t) b := &bytes.Buffer{} diff --git a/cmd/version.go b/cmd/version.go index 1c0c93c..3eed6e6 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -67,15 +67,17 @@ func latestVersion(isCli bool) (v string, err error) { } if err != nil { - return + return "", err } defer func() { - _ = res.Body.Close() + if cerr := res.Body.Close(); cerr != nil && err == nil { + err = cerr + } }() if b, err = io.ReadAll(res.Body); err != nil { - return + return "", err } if submatch := latestVersionRegexp.FindSubmatch(b); len(submatch) == 2 { diff --git a/cmd/version_test.go b/cmd/version_test.go index 4a8f6ba..f2c4cd7 100644 --- a/cmd/version_test.go +++ b/cmd/version_test.go @@ -2,7 +2,6 @@ package cmd import ( "errors" - "io/ioutil" "net/http" "os" "testing" @@ -12,39 +11,44 @@ import ( ) func Test_Version_Printer(t *testing.T) { + t.Parallel() at := assert.New(t) t.Run("success", func(t *testing.T) { + t.Parallel() httpmock.Activate() defer httpmock.DeactivateAndReset() httpmock.RegisterResponder(http.MethodGet, latestVersionURL, httpmock.NewBytesResponder(200, fakeVersionResponse)) out, err := runCobraCmd(versionCmd) - at.Nil(err) + at.NoError(err) at.Contains(out, "2.0.6") }) t.Run("latest err", func(t *testing.T) { + t.Parallel() httpmock.Activate() defer httpmock.DeactivateAndReset() httpmock.RegisterResponder(http.MethodGet, latestVersionURL, httpmock.NewBytesResponder(200, []byte("no version"))) out, err := runCobraCmd(versionCmd) - at.Nil(err) + at.NoError(err) at.Contains(out, "no version") }) } func Test_Version_Current(t *testing.T) { + t.Parallel() at := assert.New(t) t.Run("file not found", func(t *testing.T) { + t.Parallel() setupCurrentVersionFile() defer teardownCurrentVersionFile() _, err := currentVersion() - at.NotNil(err) + at.Error(err) }) t.Run("match version", func(t *testing.T) { @@ -59,7 +63,7 @@ require ( defer teardownCurrentVersionFile() v, err := currentVersion() - at.Nil(err) + at.NoError(err) at.Equal("v2.0.6", v) }) @@ -75,11 +79,12 @@ require ( defer teardownCurrentVersionFile() v, err := currentVersion() - at.Nil(err) + at.NoError(err) at.Equal("v0.0.0-20200926082917-55763e7e6ee3", v) }) t.Run("package not found", func(t *testing.T) { + t.Parallel() content := `module fiber-demo go 1.14 require ( @@ -90,52 +95,60 @@ require ( defer teardownCurrentVersionFile() _, err := currentVersion() - at.NotNil(err) + at.Error(err) }) } func setupCurrentVersionFile(content ...string) { currentVersionFile = "current-version" if len(content) > 0 { - _ = ioutil.WriteFile(currentVersionFile, []byte(content[0]), 0600) + if err := os.WriteFile(currentVersionFile, []byte(content[0]), 0o600); err != nil { + panic(err) + } } } func teardownCurrentVersionFile() { - _ = os.Remove(currentVersionFile) + if err := os.Remove(currentVersionFile); err != nil && !os.IsNotExist(err) { + panic(err) + } } func Test_Version_Latest(t *testing.T) { + t.Parallel() at := assert.New(t) t.Run("http get error", func(t *testing.T) { + t.Parallel() httpmock.Activate() defer httpmock.DeactivateAndReset() httpmock.RegisterResponder(http.MethodGet, latestVersionURL, httpmock.NewErrorResponder(errors.New("network error"))) _, err := latestVersion(false) - at.NotNil(err) + at.Error(err) }) t.Run("version matched", func(t *testing.T) { + t.Parallel() httpmock.Activate() defer httpmock.DeactivateAndReset() httpmock.RegisterResponder(http.MethodGet, latestVersionURL, httpmock.NewBytesResponder(200, fakeVersionResponse)) v, err := latestVersion(false) - at.Nil(err) + at.NoError(err) at.Equal("2.0.6", v) }) t.Run("no version matched", func(t *testing.T) { + t.Parallel() httpmock.Activate() defer httpmock.DeactivateAndReset() httpmock.RegisterResponder(http.MethodGet, latestVersionURL, httpmock.NewBytesResponder(200, []byte("no version"))) _, err := latestVersion(false) - at.NotNil(err) + at.Error(err) }) } diff --git a/fiber/main.go b/fiber/main.go index fe403aa..887b917 100644 --- a/fiber/main.go +++ b/fiber/main.go @@ -1,6 +1,8 @@ package main -import "github.com/gofiber/cli/cmd" +import ( + "github.com/gofiber/cli/cmd" +) func main() { cmd.Execute() From c6602e4ce6a2c694160e2ad2fa491135b0e5f829 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9?= Date: Sat, 12 Jul 2025 21:29:50 +0200 Subject: [PATCH 13/27] fix lint issues --- .golangci.yml | 13 ++--- cmd/dev.go | 65 ++++++++++++++++------- cmd/dev_test.go | 56 ++++++++++--------- cmd/helpers.go | 31 ++++++----- cmd/helpers_test.go | 33 ++++++------ cmd/internal/helpers.go | 9 ++-- cmd/internal/migrations/common.go | 11 ++-- cmd/internal/migrations/lists.go | 1 + cmd/internal/migrations/v3/common_test.go | 32 +++++------ cmd/internal/prompt.go | 4 +- cmd/internal/prompt_test.go | 7 +-- cmd/internal/task.go | 12 ++--- cmd/migrate.go | 10 ++-- cmd/new.go | 24 +++++---- cmd/new_test.go | 33 ++++++------ cmd/root.go | 16 +++--- cmd/root_test.go | 10 ++-- cmd/tester_test.go | 27 ++++++---- cmd/upgrade.go | 2 +- cmd/upgrade_test.go | 13 ++--- cmd/version.go | 41 +++++++++----- cmd/version_test.go | 17 +++--- 22 files changed, 256 insertions(+), 211 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index bbf51f0..8c0bc8b 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -16,8 +16,6 @@ linters-settings: deny: - pkg: "flag" desc: '`flag` package is only allowed in main.go' - - pkg: "log" - desc: 'logging is provided by `pkg/log`' - pkg: "io/ioutil" desc: '`io/ioutil` package is deprecated, use the `io` and `os` package instead' # TODO: Prevent using these without a reason @@ -56,7 +54,6 @@ linters-settings: gci: sections: - standard - - prefix(github.com/gofiber/fiber) - default - blank - dot @@ -86,7 +83,7 @@ linters-settings: # checkExported: false gofumpt: - module-path: github.com/gofiber/fiber + module-path: github.com/gofiber/cli extra-rules: true gosec: @@ -250,8 +247,6 @@ issues: exclude-case-sensitive: true max-issues-per-linter: 0 max-same-issues: 0 - exclude-dirs: - - internal # TODO: Do not ignore internal packages exclude-files: - '_msgp\.go' - '_msgp_test\.go' @@ -278,14 +273,14 @@ linters: - asciicheck - bidichk - bodyclose - - containedctx + # - containedctx TODO: Enable - contextcheck # - cyclop - decorder - depguard - dogsled # - dupl - - dupword # TODO: Enable + - dupword - durationcheck - errcheck - errchkjson @@ -346,7 +341,7 @@ linters: # - nlreturn - noctx - nolintlint - - nonamedreturns + # - nonamedreturns - nosprintfhostport # - paralleltest # TODO: Enable - perfsprint diff --git a/cmd/dev.go b/cmd/dev.go index 34d1d2a..b7aeccc 100644 --- a/cmd/dev.go +++ b/cmd/dev.go @@ -42,6 +42,10 @@ func init() { "arguments for exec") } +const ( + windowsOS = "windows" +) + // devCmd reruns the fiber project if watched files changed var devCmd = &cobra.Command{ Use: "dev", @@ -57,7 +61,6 @@ func devRunE(_ *cobra.Command, _ []string) error { type config struct { root string target string - binPath string extensions []string excludeDirs []string excludeFiles []string @@ -136,7 +139,7 @@ func (e *escort) run() error { func (e *escort) init() error { var err error if e.w, err = fsnotify.NewWatcher(); err != nil { - return err + return fmt.Errorf("failed to create watcher: %w", err) } e.watcherEvents = e.w.Events @@ -146,20 +149,20 @@ func (e *escort) init() error { // normalize root if e.root, err = filepath.Abs(e.root); err != nil { - return err + return fmt.Errorf("failed to get abs path for root: %w", err) } // create bin target f, err := os.CreateTemp("", "") if err != nil { - return err + return fmt.Errorf("failed to create temp file: %w", err) } if cerr := f.Close(); cerr != nil { - return cerr + return fmt.Errorf("failed to close temp file: %w", cerr) } e.binPath = f.Name() - if runtime.GOOS == "windows" { + if runtime.GOOS == windowsOS { e.binPath += ".exe" } @@ -248,8 +251,10 @@ func (e *escort) watchingBin() { } func (e *escort) runBin() { - if ok := e.compiling.Load(); ok != nil && ok.(bool) { - return + if ok := e.compiling.Load(); ok != nil { + if val, ok := ok.(bool); ok && val { + return + } } e.doPreRun() @@ -293,10 +298,14 @@ func (e *escort) runBin() { func (e *escort) cleanOldBin() { defer func() { if e.stdoutPipe != nil { - _ = e.stdoutPipe.Close() + if err := e.stdoutPipe.Close(); err != nil { + log.Printf("Failed to close stdout pipe: %v", err) + } } if e.stderrPipe != nil { - _ = e.stderrPipe.Close() + if err := e.stderrPipe.Close(); err != nil { + log.Printf("Failed to close stderr pipe: %v", err) + } } }() @@ -304,11 +313,13 @@ func (e *escort) cleanOldBin() { log.Println("Killing old pid", pid) var err error - if runtime.GOOS == "windows" { + if runtime.GOOS == windowsOS { err = execCommand("TASKKILL", "/T", "/F", "/PID", strconv.Itoa(pid)).Run() } else { err = e.bin.Process.Kill() - _, _ = e.bin.Process.Wait() + if _, waitErr := e.bin.Process.Wait(); waitErr != nil { + log.Printf("Failed to wait for process %d: %v", pid, waitErr) + } } if err != nil { @@ -323,13 +334,21 @@ func (e *escort) watchingPipes() { if e.stdoutPipe, err = e.bin.StdoutPipe(); err != nil { log.Printf("Failed to get stdout pipe: %s", err) } else { - go func() { _, _ = io.Copy(os.Stdout, e.stdoutPipe) }() + go func() { + if _, err := io.Copy(os.Stdout, e.stdoutPipe); err != nil { + log.Printf("Failed to copy stdout: %v", err) + } + }() } if e.stderrPipe, err = e.bin.StderrPipe(); err != nil { log.Printf("Failed to get stderr pipe: %s", err) } else { - go func() { _, _ = io.Copy(os.Stderr, e.stderrPipe) }() + go func() { + if _, err := io.Copy(os.Stderr, e.stderrPipe); err != nil { + log.Printf("Failed to copy stderr: %v", err) + } + }() } } @@ -407,12 +426,20 @@ func (e *escort) doPreRun() { cmd := execCommand(command[0], command[1:]...) out, err := cmd.CombinedOutput() var buf bytes.Buffer - _, _ = buf.WriteString(fmt.Sprintf("Pre running %s... ", command)) + if _, werr := buf.WriteString(fmt.Sprintf("Pre running %s... ", command)); werr != nil { + log.Printf("Failed to write to buffer: %v", werr) + } if err != nil { - _, _ = buf.WriteString(err.Error()) - _, _ = buf.WriteString(":") + if _, werr := buf.WriteString(err.Error()); werr != nil { + log.Printf("Failed to write error to buffer: %v", werr) + } + if _, werr := buf.WriteString(":"); werr != nil { + log.Printf("Failed to write colon to buffer: %v", werr) + } + } + if _, werr := buf.Write(out); werr != nil { + log.Printf("Failed to write output to buffer: %v", werr) } - _, _ = buf.Write(out) log.Print(buf.String()) } } @@ -435,7 +462,7 @@ func parsePreRunCommands(commands []string) (list [][]string) { list = append(list, r) } } - return + return list } const ( diff --git a/cmd/dev_test.go b/cmd/dev_test.go index 4f0efa3..c2dfe64 100644 --- a/cmd/dev_test.go +++ b/cmd/dev_test.go @@ -3,7 +3,7 @@ package cmd import ( "context" "errors" - "io/ioutil" + "io" "os" "os/exec" "path/filepath" @@ -16,6 +16,7 @@ import ( "github.com/fsnotify/fsnotify" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func Test_Dev_Escort_New(t *testing.T) { @@ -30,28 +31,26 @@ func Test_Dev_Escort_Init(t *testing.T) { at := assert.New(t) e := getEscort() - at.Nil(e.init()) + require.NoError(t, e.init()) at.Contains(e.root, "cli") at.NotEmpty(e.binPath) - if runtime.GOOS != "windows" { - at.Nil(os.Remove(e.binPath)) + if runtime.GOOS != windowsOS { + require.NoError(t, os.Remove(e.binPath)) } } func Test_Dev_Escort_Run(t *testing.T) { - at := assert.New(t) - setupCmd() defer teardownCmd() e := getEscort() var err error - e.root, err = ioutil.TempDir("", "test_run") - at.Nil(err) + e.root, err = os.MkdirTemp("", "test_run") + require.NoError(t, err) defer func() { - at.Nil(os.RemoveAll(e.root)) + require.NoError(t, os.RemoveAll(e.root)) }() e.sig = make(chan os.Signal, 1) @@ -61,7 +60,7 @@ func Test_Dev_Escort_Run(t *testing.T) { e.sig <- syscall.SIGINT }() - at.Nil(e.run()) + require.NoError(t, e.run()) } func Test_Dev_Escort_RunBin(t *testing.T) { @@ -72,9 +71,9 @@ func Test_Dev_Escort_RunBin(t *testing.T) { e.bin = exec.Command("go", "version") _, err := e.bin.CombinedOutput() - assert.Nil(t, err) + require.NoError(t, err) - rc := ioutil.NopCloser(strings.NewReader("")) + rc := io.NopCloser(strings.NewReader("")) e.stdoutPipe = rc e.stderrPipe = rc @@ -87,7 +86,7 @@ func Test_Dev_Escort_WatchingPipes(t *testing.T) { e := getEscort() e.bin = exec.Command("go", "version") _, err := e.bin.CombinedOutput() - assert.Nil(t, err) + require.NoError(t, err) e.watchingPipes() } @@ -123,32 +122,31 @@ func Test_Dev_Escort_WatchingFiles(t *testing.T) { e.hitCh = make(chan struct{}) e.w, err = fsnotify.NewWatcher() - at.Nil(err) + require.NoError(t, err) e.extensions = []string{"go"} e.watcherEvents = make(chan fsnotify.Event) e.watcherErrors = make(chan error) - e.root, err = ioutil.TempDir("", "test_watch") - at.Nil(err) + e.root, err = os.MkdirTemp("", "test_watch") + require.NoError(t, err) defer func() { - _ = os.RemoveAll(e.root) + require.NoError(t, os.RemoveAll(e.root)) }() - _, err = ioutil.TempDir(e.root, ".git") - at.Nil(err) + _, err = os.MkdirTemp(e.root, ".git") + require.NoError(t, err) - newDir, err := ioutil.TempDir(e.root, "") - at.Nil(err) + newDir, err := os.MkdirTemp(e.root, "") + require.NoError(t, err) - ignoredFile, err := ioutil.TempFile(e.root, "") - at.Nil(err) - defer func() { at.Nil(ignoredFile.Close()) }() - e.excludeFiles = []string{filepath.Base(ignoredFile.Name())} + ignoredFile, err := os.MkdirTemp(e.root, "") + require.NoError(t, err) + e.excludeFiles = []string{filepath.Base(ignoredFile)} - f, err := ioutil.TempFile(e.root, "*.go") - at.Nil(err) - defer func() { at.Nil(f.Close()) }() + f, err := os.CreateTemp(e.root, "*.go") + require.NoError(t, err) + defer func() { require.NoError(t, f.Close()) }() name := f.Name() go e.watchingFiles() @@ -164,7 +162,7 @@ func Test_Dev_Escort_WatchingFiles(t *testing.T) { at.Fail("should hit") } - e.watcherEvents <- fsnotify.Event{Name: ignoredFile.Name(), Op: fsnotify.Create} + e.watcherEvents <- fsnotify.Event{Name: ignoredFile, Op: fsnotify.Create} e.watcherEvents <- fsnotify.Event{Name: name, Op: fsnotify.Create} e.terminate() diff --git a/cmd/helpers.go b/cmd/helpers.go index 9ddfab6..931eacd 100644 --- a/cmd/helpers.go +++ b/cmd/helpers.go @@ -27,14 +27,13 @@ func init() { } func runCmd(cmd *exec.Cmd) (err error) { - var ( stderr io.ReadCloser stdout io.ReadCloser ) if stderr, err = cmd.StderrPipe(); err != nil { - return + return err } defer func() { _ = stderr.Close() @@ -42,7 +41,7 @@ func runCmd(cmd *exec.Cmd) (err error) { go func() { _, _ = io.Copy(os.Stderr, stderr) }() if stdout, err = cmd.StdoutPipe(); err != nil { - return + return err } defer func() { _ = stdout.Close() @@ -53,7 +52,7 @@ func runCmd(cmd *exec.Cmd) (err error) { err = fmt.Errorf("failed to run %s", cmd.String()) } - return + return err } // replaces matching file patterns in a path, including subdirectories @@ -72,7 +71,7 @@ func replace(path, pattern, old, new string) error { func replaceWalkFn(path string, info os.FileInfo, pattern string, old, new []byte) (err error) { var matched bool if matched, err = filepath.Match(pattern, info.Name()); err != nil { - return + return err } if matched { @@ -80,28 +79,28 @@ func replaceWalkFn(path string, info os.FileInfo, pattern string, old, new []byt var oldContent []byte if oldContent, err = os.ReadFile(cleanedPath); err != nil { - return + return err } if err = os.WriteFile(cleanedPath, bytes.Replace(oldContent, old, new, -1), 0); err != nil { - return + return err } } - return + return nil } func createFile(filePath, content string) (err error) { var f *os.File if f, err = os.Create(filepath.Clean(filePath)); err != nil { - return + return err } defer func() { _ = f.Close() }() _, err = f.WriteString(content) - return + return err } func formatLatency(d time.Duration) time.Duration { @@ -122,15 +121,15 @@ func loadConfig() (err error) { if fileExist(configFilePath) { if err = loadJson(configFilePath, &rc); err != nil { - return + return err } } - return + return nil } func storeConfig() { - _ = storeJson(configFilePath(), rc) + _ = storeJSON(configFilePath(), rc) } func configFilePath() string { @@ -148,16 +147,16 @@ var fileExist = func(filename string) bool { return true } -func storeJson(filename string, v interface{}) error { +func storeJSON(filename string, v any) error { b, err := json.MarshalIndent(v, "", " ") if err != nil { return err } - return os.WriteFile(filename, b, 0600) + return os.WriteFile(filename, b, 0o600) } -func loadJson(filename string, v interface{}) error { +func loadJson(filename string, v any) error { b, err := os.ReadFile(path.Clean(filename)) if err != nil { return err diff --git a/cmd/helpers_test.go b/cmd/helpers_test.go index 853fbfd..c7c9553 100644 --- a/cmd/helpers_test.go +++ b/cmd/helpers_test.go @@ -2,12 +2,12 @@ package cmd import ( "fmt" - "io/ioutil" "os" "testing" "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func Test_Helpers_FormatLatency(t *testing.T) { @@ -37,28 +37,25 @@ func Test_Helpers_FormatLatency(t *testing.T) { func Test_Helper_Replace(t *testing.T) { t.Parallel() - at := assert.New(t) - - dir, err := ioutil.TempDir("", "test_helper_replace") - at.Nil(err) + dir, err := os.MkdirTemp("", "test_helper_replace") + require.NoError(t, err) defer func() { - at.Nil(os.RemoveAll(dir)) + require.NoError(t, os.RemoveAll(dir)) }() - f, err := ioutil.TempFile(dir, "*.go") - at.Nil(err) - at.Nil(f.Close()) + f, err := os.CreateTemp(dir, "*.go") + require.NoError(t, err) + require.NoError(t, f.Close()) - at.Nil(replace(dir, "*.go", "old", "new")) + require.NoError(t, replace(dir, "*.go", "old", "new")) } func Test_Helper_LoadConfig(t *testing.T) { t.Parallel() - at := assert.New(t) t.Run("no config file", func(t *testing.T) { t.Parallel() - at.Nil(loadConfig()) + require.NoError(t, loadConfig()) }) t.Run("has config file", func(t *testing.T) { @@ -74,19 +71,19 @@ func Test_Helper_LoadConfig(t *testing.T) { filename := fmt.Sprintf("%s%c%s", homeDir, os.PathSeparator, configName) f, err := os.Create(filename) - at.Nil(err) - defer func() { at.Nil(f.Close()) }() + require.NoError(t, err) + defer func() { require.NoError(t, f.Close()) }() _, err = f.WriteString("{}") - at.Nil(err) + require.NoError(t, err) - at.Nil(loadConfig()) + require.NoError(t, loadConfig()) }) } -func Test_Helper_StoreJson(t *testing.T) { +func Test_Helper_StoreJSON(t *testing.T) { t.Parallel() - assert.NotNil(t, storeJson("", complex(1, 1))) + require.Error(t, storeJSON("", complex(1, 1))) } func Test_Helper_ConfigFilePath(t *testing.T) { diff --git a/cmd/internal/helpers.go b/cmd/internal/helpers.go index 77f81e3..2eb75ae 100644 --- a/cmd/internal/helpers.go +++ b/cmd/internal/helpers.go @@ -37,13 +37,13 @@ func ChangeFileContent(cwd string, processorFn FileProcessor) error { // change go files in project err := filepath.Walk(cwd, func(path string, info os.FileInfo, err error) error { if err != nil { - //fmt.Printf("Error while traversing %s: %v\n", path, err) + // fmt.Printf("Error while traversing %s: %v\n", path, err) return err } // Skip directories named "vendor" if info.IsDir() && info.Name() == "vendor" { - //fmt.Printf("Skipping directory: %s\n", path) + // fmt.Printf("Skipping directory: %s\n", path) return filepath.SkipDir } @@ -51,17 +51,16 @@ func ChangeFileContent(cwd string, processorFn FileProcessor) error { if info.IsDir() || !strings.HasSuffix(info.Name(), ".go") { return nil } - //fmt.Printf("Processing Go file: %s\n", path) + // fmt.Printf("Processing Go file: %s\n", path) fileContent, err := os.ReadFile(path) // update go.mod file - if err2 := os.WriteFile(path, []byte(processorFn(string(fileContent))), 0644); err != nil { + if err2 := os.WriteFile(path, []byte(processorFn(string(fileContent))), 0o644); err != nil { return err2 } return nil }) - if err != nil { return fmt.Errorf("Error while traversing the directory tree: %v\n", err) } diff --git a/cmd/internal/migrations/common.go b/cmd/internal/migrations/common.go index 74e041c..6893958 100644 --- a/cmd/internal/migrations/common.go +++ b/cmd/internal/migrations/common.go @@ -2,19 +2,18 @@ package migrations import ( "fmt" - semver "github.com/Masterminds/semver/v3" - "github.com/spf13/cobra" "os" "regexp" "strconv" "strings" + semver "github.com/Masterminds/semver/v3" + "github.com/spf13/cobra" + "github.com/gofiber/cli/cmd/internal" ) -var ( - pkgRegex = regexp.MustCompile(`(github\.com\/gofiber\/fiber\/)(v\d+)( *?)(v[\w.-]+)`) -) +var pkgRegex = regexp.MustCompile(`(github\.com\/gofiber\/fiber\/)(v\d+)( *?)(v[\w.-]+)`) func MigrateGoPkgs(cmd *cobra.Command, cwd string, curr *semver.Version, target *semver.Version) error { pkgReplacer := strings.NewReplacer( @@ -43,7 +42,7 @@ func MigrateGoPkgs(cmd *cobra.Command, cwd string, curr *semver.Version, target ) // update go.mod file - if err := os.WriteFile(modFile, []byte(fileContentStr), 0644); err != nil { + if err := os.WriteFile(modFile, []byte(fileContentStr), 0o644); err != nil { return err } diff --git a/cmd/internal/migrations/lists.go b/cmd/internal/migrations/lists.go index 61ae73b..62fd9fe 100644 --- a/cmd/internal/migrations/lists.go +++ b/cmd/internal/migrations/lists.go @@ -2,6 +2,7 @@ package migrations import ( "fmt" + semver "github.com/Masterminds/semver/v3" "github.com/spf13/cobra" diff --git a/cmd/internal/migrations/v3/common_test.go b/cmd/internal/migrations/v3/common_test.go index f07213e..1267bd8 100644 --- a/cmd/internal/migrations/v3/common_test.go +++ b/cmd/internal/migrations/v3/common_test.go @@ -2,27 +2,27 @@ package v3 import ( "bytes" - "io/ioutil" "os" "path/filepath" "testing" "github.com/spf13/cobra" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func writeTempFile(t *testing.T, dir, name, content string) string { t.Helper() path := filepath.Join(dir, name) err := os.WriteFile(path, []byte(content), 0o644) - assert.Nil(t, err) + require.NoError(t, err) return path } func readFile(t *testing.T, path string) string { t.Helper() b, err := os.ReadFile(path) - assert.Nil(t, err) + require.NoError(t, err) return string(b) } @@ -36,8 +36,8 @@ func newCmd(buf *bytes.Buffer) *cobra.Command { func Test_MigrateHandlerSignatures(t *testing.T) { t.Parallel() - dir, err := ioutil.TempDir("", "mhstest") - assert.Nil(t, err) + dir, err := os.MkdirTemp("", "mhstest") + require.NoError(t, err) defer os.RemoveAll(dir) file := writeTempFile(t, dir, "main.go", `package main @@ -47,7 +47,7 @@ func handler(c *fiber.Ctx) error { return nil } var buf bytes.Buffer cmd := newCmd(&buf) - assert.Nil(t, MigrateHandlerSignatures(cmd, dir, nil, nil)) + require.NoError(t, MigrateHandlerSignatures(cmd, dir, nil, nil)) content := readFile(t, file) assert.NotContains(t, content, "*fiber.Ctx") @@ -58,14 +58,14 @@ func handler(c *fiber.Ctx) error { return nil } func Test_MigrateParserMethods(t *testing.T) { t.Parallel() - dir, err := ioutil.TempDir("", "mptest") - assert.Nil(t, err) + dir, err := os.MkdirTemp("", "mptest") + require.NoError(t, err) defer os.RemoveAll(dir) file := writeTempFile(t, dir, "main.go", `package main import "github.com/gofiber/fiber/v2" func handler(c fiber.Ctx) error { - var v interface{} + var v any c.BodyParser(&v) c.CookieParser(&v) c.ParamsParser(&v) @@ -76,7 +76,7 @@ func handler(c fiber.Ctx) error { var buf bytes.Buffer cmd := newCmd(&buf) - assert.Nil(t, MigrateParserMethods(cmd, dir, nil, nil)) + require.NoError(t, MigrateParserMethods(cmd, dir, nil, nil)) content := readFile(t, file) assert.Contains(t, content, ".Bind().Body(&v)") @@ -89,8 +89,8 @@ func handler(c fiber.Ctx) error { func Test_MigrateRedirectMethods(t *testing.T) { t.Parallel() - dir, err := ioutil.TempDir("", "mrtest") - assert.Nil(t, err) + dir, err := os.MkdirTemp("", "mrtest") + require.NoError(t, err) defer os.RemoveAll(dir) file := writeTempFile(t, dir, "main.go", `package main @@ -105,7 +105,7 @@ func handler(c fiber.Ctx) error { var buf bytes.Buffer cmd := newCmd(&buf) - assert.Nil(t, MigrateRedirectMethods(cmd, dir, nil, nil)) + require.NoError(t, MigrateRedirectMethods(cmd, dir, nil, nil)) content := readFile(t, file) assert.Contains(t, content, ".Redirect().To(\"/foo\")") @@ -117,8 +117,8 @@ func handler(c fiber.Ctx) error { func Test_MigrateGenericHelpers(t *testing.T) { t.Parallel() - dir, err := ioutil.TempDir("", "mghtest") - assert.Nil(t, err) + dir, err := os.MkdirTemp("", "mghtest") + require.NoError(t, err) defer os.RemoveAll(dir) file := writeTempFile(t, dir, "main.go", `package main @@ -134,7 +134,7 @@ func handler(c fiber.Ctx) error { var buf bytes.Buffer cmd := newCmd(&buf) - assert.Nil(t, MigrateGenericHelpers(cmd, dir, nil, nil)) + require.NoError(t, MigrateGenericHelpers(cmd, dir, nil, nil)) content := readFile(t, file) assert.Contains(t, content, "fiber.Params[int](c, \"id\"") diff --git a/cmd/internal/prompt.go b/cmd/internal/prompt.go index 6d5ae8a..d4e9352 100644 --- a/cmd/internal/prompt.go +++ b/cmd/internal/prompt.go @@ -22,7 +22,7 @@ type Prompt struct { func NewPrompt(title string, placeholder ...string) *Prompt { p := &Prompt{ title: title, - textInput: input.NewModel(), + textInput: input.New(), } if len(placeholder) > 0 { @@ -56,7 +56,7 @@ func (p *Prompt) Answer() (result string, err error) { return } - if err := p.p.Start(); err != nil { + if _, err := p.p.Run(); err != nil { return "", err } return p.answer, nil diff --git a/cmd/internal/prompt_test.go b/cmd/internal/prompt_test.go index e96308b..717b8e3 100644 --- a/cmd/internal/prompt_test.go +++ b/cmd/internal/prompt_test.go @@ -6,6 +6,7 @@ import ( tea "github.com/charmbracelet/bubbletea" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func Test_Prompt_New(t *testing.T) { @@ -26,7 +27,7 @@ func Test_Prompt_Answer(t *testing.T) { p := NewPrompt("") _, err := p.Answer() - assert.NotNil(t, err) + require.Error(t, err) } func Test_Prompt_YesOrNo(t *testing.T) { @@ -34,7 +35,7 @@ func Test_Prompt_YesOrNo(t *testing.T) { p := NewPrompt("") _, err := p.YesOrNo() - assert.NotNil(t, err) + require.Error(t, err) } func Test_Prompt_ParseBool(t *testing.T) { @@ -67,7 +68,7 @@ func Test_Prompt_Update(t *testing.T) { at.Nil(cmd) _, cmd = p.Update(errMsg(errors.New("fake error"))) - at.NotNil(p.err) + require.Error(t, p.err) at.Nil(cmd) _, cmd = p.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'a'}}) diff --git a/cmd/internal/task.go b/cmd/internal/task.go index 665bcd4..cf9e460 100644 --- a/cmd/internal/task.go +++ b/cmd/internal/task.go @@ -19,7 +19,7 @@ type SpinnerTask struct { } func NewSpinnerTask(title string, task Task) *SpinnerTask { - spinnerModel := spinner.NewModel() + spinnerModel := spinner.New() spinnerModel.Spinner = spinner.Dot at := &SpinnerTask{ @@ -37,12 +37,11 @@ func (t *SpinnerTask) Init() tea.Cmd { return tea.Batch( func() tea.Msg { return finishedMsg{t.task()} - }, spinner.Tick) + }, t.spinnerModel.Tick) } func (t *SpinnerTask) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { - case tea.KeyMsg: switch msg.String() { case "q", "esc", "ctrl+c": @@ -61,7 +60,6 @@ func (t *SpinnerTask) Update(msg tea.Msg) (tea.Model, tea.Cmd) { t.spinnerModel, cmd = t.spinnerModel.Update(msg) return t, cmd } - } func (t *SpinnerTask) View() string { @@ -79,11 +77,11 @@ func (t *SpinnerTask) View() string { func (t *SpinnerTask) Run() (err error) { if _, err = checkConsole(); err != nil { - return + return err } - if err = t.p.Start(); err != nil { - return + if _, err = t.p.Run(); err != nil { + return err } return t.err diff --git a/cmd/migrate.go b/cmd/migrate.go index b46f3f0..abd0411 100644 --- a/cmd/migrate.go +++ b/cmd/migrate.go @@ -2,21 +2,21 @@ package cmd import ( "fmt" + "os" + "strings" + "github.com/Masterminds/semver/v3" "github.com/gofiber/cli/cmd/internal/migrations" "github.com/muesli/termenv" "github.com/spf13/cobra" - "os" - "strings" ) var ( - targetVersionS string - latestFiberVersion string + targetVersionS string ) func init() { - latestFiberVersion, _ := latestVersion(false) + latestFiberVersion, _ := LatestFiberVersion() migrateCmd.Flags().StringVarP(&targetVersionS, "to", "t", "", "Migrate to a specific version e.g: "+latestFiberVersion+" Format: X.Y.Z") migrateCmd.MarkFlagRequired("to") diff --git a/cmd/new.go b/cmd/new.go index 21f2aff..89a03cd 100644 --- a/cmd/new.go +++ b/cmd/new.go @@ -41,7 +41,7 @@ func newRunE(cmd *cobra.Command, args []string) (err error) { projectPath := fmt.Sprintf("%s%c%s", wd, os.PathSeparator, projectName) if err = createProject(projectPath); err != nil { - return + return err } defer func() { if err != nil { @@ -65,8 +65,8 @@ func newRunE(cmd *cobra.Command, args []string) (err error) { } func createProject(projectPath string) (err error) { - if err = os.Mkdir(projectPath, 0750); err != nil { - return + if err = os.Mkdir(projectPath, 0o750); err != nil { + return err } return os.Chdir(projectPath) @@ -75,21 +75,23 @@ func createProject(projectPath string) (err error) { func createBasic(projectPath, modName string) (err error) { // create main.go if err = createFile(fmt.Sprintf("%s%cmain.go", projectPath, os.PathSeparator), newBasicTemplate); err != nil { - return + return err } return runCmd(execCommand("go", "mod", "init", modName)) } -const githubPrefix = "https://github.com/" -const defaultRepo = "gofiber/boilerplate" +const ( + githubPrefix = "https://github.com/" + defaultRepo = "gofiber/boilerplate" +) var fullPathRegex = regexp.MustCompile(`^(http|https|git)`) func createComplex(projectPath, modName string) (err error) { var git string if git, err = execLookPath("git"); err != nil { - return + return err } toClone := githubPrefix + repo @@ -98,19 +100,19 @@ func createComplex(projectPath, modName string) (err error) { } if err = runCmd(execCommand(git, "clone", toClone, projectPath)); err != nil { - return + return err } if repo == defaultRepo { if err = replace(projectPath, "go.mod", "boilerplate", modName); err != nil { - return + return err } if err = replace(projectPath, "*.go", "boilerplate", modName); err != nil { - return + return err } } - return + return err } var ( diff --git a/cmd/new_test.go b/cmd/new_test.go index 5dae83d..0554023 100644 --- a/cmd/new_test.go +++ b/cmd/new_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func Test_New_Run(t *testing.T) { @@ -12,7 +13,7 @@ func Test_New_Run(t *testing.T) { t.Run("new project", func(t *testing.T) { defer func() { - at.Nil(os.Chdir("../")) + require.NoError(t, os.Chdir("../")) _ = os.RemoveAll("normal") }() @@ -21,14 +22,14 @@ func Test_New_Run(t *testing.T) { out, err := runCobraCmd(newCmd, "normal") - at.Nil(err) + require.NoError(t, err) at.Contains(out, "Done") }) t.Run("custom mod name", func(t *testing.T) { defer func() { - at.Nil(os.Chdir("../")) - at.Nil(os.RemoveAll("custom_mod_name")) + require.NoError(t, os.Chdir("../")) + require.NoError(t, os.RemoveAll("custom_mod_name")) }() setupCmd() @@ -36,28 +37,28 @@ func Test_New_Run(t *testing.T) { out, err := runCobraCmd(newCmd, "custom_mod_name", "name") - at.Nil(err) + require.NoError(t, err) at.Contains(out, "name") }) t.Run("create complex project", func(t *testing.T) { defer func() { - at.Nil(os.Chdir("../")) - at.Nil(os.RemoveAll("complex")) + require.NoError(t, os.Chdir("../")) + require.NoError(t, os.RemoveAll("complex")) }() setupCmd() defer teardownCmd() out, err := runCobraCmd(newCmd, "complex", "-t=complex") - at.Nil(err) + require.NoError(t, err) at.Contains(out, "Done") }) t.Run("failed to create complex project", func(t *testing.T) { defer func() { - at.Nil(os.Chdir("../")) - at.Nil(os.RemoveAll("complex_failed")) + require.NoError(t, os.Chdir("../")) + require.NoError(t, os.RemoveAll("complex_failed")) }() setupCmd(errFlag) @@ -65,30 +66,28 @@ func Test_New_Run(t *testing.T) { out, err := runCobraCmd(newCmd, "complex_failed", "-t=complex") - at.NotNil(err) + require.Error(t, err) at.Contains(out, "failed to run") }) t.Run("invalid project name", func(t *testing.T) { out, err := runCobraCmd(newCmd, ".") - at.NotNil(err) + require.Error(t, err) at.Contains(out, ".") }) } func Test_New_CreateBasic(t *testing.T) { - assert.NotNil(t, createBasic(" ", "name")) + require.Error(t, createBasic(" ", "name")) } func Test_New_CreateComplex(t *testing.T) { - at := assert.New(t) - t.Run("look path error", func(t *testing.T) { setupLookPath(errFlag) defer teardownLookPath() - at.NotNil(createComplex(" ", "name")) + require.Error(t, createComplex(" ", "name")) }) t.Run("failed to replace pattern", func(t *testing.T) { @@ -99,6 +98,6 @@ func Test_New_CreateComplex(t *testing.T) { repo = "git@any.provider.com:id/repo.git" - at.NotNil(createComplex(" ", "name")) + require.Error(t, createComplex(" ", "name")) }) } diff --git a/cmd/root.go b/cmd/root.go index 30cd557..0e9145d 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -9,15 +9,15 @@ import ( "github.com/spf13/cobra" ) -const version = "0.0.9" -const configName = ".fiberconfig" - -var ( - rc = rootConfig{ - CliVersionCheckInterval: int64((time.Hour * 12) / time.Second), - } +const ( + version = "0.0.9" + configName = ".fiberconfig" ) +var rc = rootConfig{ + CliVersionCheckInterval: int64((time.Hour * 12) / time.Second), +} + type rootConfig struct { CliVersionCheckInterval int64 `json:"cli_version_check_interval"` CliVersionCheckedAt int64 `json:"cli_version_checked_at"` @@ -68,7 +68,7 @@ func checkCliVersion(cmd *cobra.Command) { return } - cliLatestVersion, err := latestVersion(true) + cliLatestVersion, err := LatestCliVersion() if err != nil { return } diff --git a/cmd/root_test.go b/cmd/root_test.go index 094f785..6bc1f8a 100644 --- a/cmd/root_test.go +++ b/cmd/root_test.go @@ -11,6 +11,7 @@ import ( "github.com/jarcoal/httpmock" "github.com/spf13/cobra" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func Test_Root_Execute(t *testing.T) { @@ -35,7 +36,7 @@ func Test_Root_Execute(t *testing.T) { func Test_Root_RunE(t *testing.T) { at, b := setupRootCmd(t) - at.Nil(rootRunE(rootCmd, nil)) + require.NoError(t, rootRunE(rootCmd, nil)) at.Contains(b.String(), "fiber") } @@ -81,7 +82,7 @@ func Test_Root_CheckCliVersion(t *testing.T) { httpmock.Activate() defer httpmock.DeactivateAndReset() - httpmock.RegisterResponder(http.MethodGet, latestCliVersionUrl, httpmock.NewErrorResponder(errors.New("network error"))) + httpmock.RegisterResponder(http.MethodGet, latestCliVersionURL, httpmock.NewErrorResponder(errors.New("network error"))) checkCliVersion(rootCmd) @@ -95,7 +96,7 @@ func Test_Root_CheckCliVersion(t *testing.T) { teardownHomeDir(tempHome) }() - httpmock.RegisterResponder(http.MethodGet, latestCliVersionUrl, httpmock.NewBytesResponder(200, fakeCliVersionResponse())) + httpmock.RegisterResponder(http.MethodGet, latestCliVersionURL, httpmock.NewBytesResponder(200, fakeCliVersionResponse())) checkCliVersion(rootCmd) @@ -113,6 +114,7 @@ func Test_Root_NeedCheckCliVersion(t *testing.T) { } func setupRootCmd(t *testing.T) (*assert.Assertions, *bytes.Buffer) { + t.Helper() at := assert.New(t) b := &bytes.Buffer{} @@ -122,7 +124,7 @@ func setupRootCmd(t *testing.T) (*assert.Assertions, *bytes.Buffer) { return at, b } -var latestCliVersionUrl = "https://api.github.com/repos/gofiber/cli/releases/latest" +var latestCliVersionURL = "https://api.github.com/repos/gofiber/cli/releases/latest" var fakeCliVersionResponse = func(version ...string) []byte { v := "99.99.99" diff --git a/cmd/tester_test.go b/cmd/tester_test.go index 32b387e..e7b96fe 100644 --- a/cmd/tester_test.go +++ b/cmd/tester_test.go @@ -4,7 +4,6 @@ import ( "bytes" "errors" "fmt" - "io/ioutil" "os" "os/exec" "testing" @@ -17,11 +16,13 @@ import ( var ( needError bool errFlag = struct{}{} + testExit = os.Exit // for testing exit ) func fakeExecCommand(command string, args ...string) *exec.Cmd { cs := []string{"-test.run=TestHelperProcess", "--", command} cs = append(cs, args...) + // gosec: G204 - safe for test, args are controlled cmd := exec.Command(os.Args[0], cs...) cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"} if needError { @@ -46,15 +47,17 @@ func TestHelperProcess(t *testing.T) { if len(args) == 0 { _, _ = fmt.Fprintf(os.Stderr, "No command") - os.Exit(2) + testExit(2) + return } if os.Getenv("GO_WANT_HELPER_NEED_ERR") == "1" { _, _ = fmt.Fprintf(os.Stderr, "fake error") - os.Exit(1) + testExit(1) + return } - os.Exit(0) + testExit(0) } func setupCmd(flag ...struct{}) { @@ -70,11 +73,11 @@ func teardownCmd() { } func setupLookPath(flag ...struct{}) { - execLookPath = func(file string) (s string, err error) { + execLookPath = func(_ string) (s string, err error) { if len(flag) > 0 { err = errors.New("fake look path error") } - return + return "", err } } @@ -88,10 +91,12 @@ func setupOsExit(override ...func(int)) { fn = override[0] } osExit = fn + testExit = fn } func teardownOsExit() { osExit = os.Exit + testExit = os.Exit } func runCobraCmd(cmd *cobra.Command, args ...string) (string, error) { @@ -107,13 +112,17 @@ func runCobraCmd(cmd *cobra.Command, args ...string) (string, error) { } func setupHomeDir(t *testing.T, pattern string) string { - homeDir, err := ioutil.TempDir("", "test_"+pattern) - assert.Nil(t, err) + t.Helper() + homeDir, err := os.MkdirTemp("", "test_"+pattern) + assert.NoError(t, err) return homeDir } func teardownHomeDir(dir string) { - _ = os.RemoveAll(dir) + err := os.RemoveAll(dir) + if err != nil { + fmt.Fprintf(os.Stderr, "failed to remove temp dir: %v", err) + } } func setupSpinner() { diff --git a/cmd/upgrade.go b/cmd/upgrade.go index 128d641..a2b338a 100644 --- a/cmd/upgrade.go +++ b/cmd/upgrade.go @@ -18,7 +18,7 @@ var upgradeCmd = &cobra.Command{ var upgraded bool func upgradeRunE(cmd *cobra.Command, _ []string) error { - cliLatestVersion, err := latestVersion(true) + cliLatestVersion, err := LatestCliVersion() if err != nil { return err } diff --git a/cmd/upgrade_test.go b/cmd/upgrade_test.go index 90c10f8..1c6c16e 100644 --- a/cmd/upgrade_test.go +++ b/cmd/upgrade_test.go @@ -8,6 +8,7 @@ import ( "github.com/jarcoal/httpmock" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func Test_Upgrade_upgradeRunE(t *testing.T) { @@ -21,24 +22,24 @@ func Test_Upgrade_upgradeRunE(t *testing.T) { httpmock.Activate() defer httpmock.DeactivateAndReset() - httpmock.RegisterResponder(http.MethodGet, latestCliVersionUrl, httpmock.NewErrorResponder(errors.New("network error"))) + httpmock.RegisterResponder(http.MethodGet, latestCliVersionURL, httpmock.NewErrorResponder(errors.New("network error"))) - at.NotNil(upgradeRunE(upgradeCmd, nil)) + require.Error(t, upgradeRunE(upgradeCmd, nil)) - httpmock.RegisterResponder(http.MethodGet, latestCliVersionUrl, httpmock.NewBytesResponder(200, fakeCliVersionResponse())) + httpmock.RegisterResponder(http.MethodGet, latestCliVersionURL, httpmock.NewBytesResponder(200, fakeCliVersionResponse())) setupSpinner() defer teardownSpinner() - at.Nil(upgradeRunE(upgradeCmd, nil)) + require.NoError(t, upgradeRunE(upgradeCmd, nil)) at.Contains(b.String(), "99.99.99") - httpmock.RegisterResponder(http.MethodGet, latestCliVersionUrl, httpmock.NewBytesResponder(200, fakeCliVersionResponse(version))) + httpmock.RegisterResponder(http.MethodGet, latestCliVersionURL, httpmock.NewBytesResponder(200, fakeCliVersionResponse(version))) b.Reset() - at.Nil(upgradeRunE(upgradeCmd, nil)) + require.NoError(t, upgradeRunE(upgradeCmd, nil)) at.Contains(b.String(), "Currently") } diff --git a/cmd/version.go b/cmd/version.go index 3eed6e6..c854bdd 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -1,12 +1,14 @@ package cmd import ( + "context" "errors" "fmt" "io" "net/http" "os" "regexp" + "time" "github.com/spf13/cobra" ) @@ -28,7 +30,7 @@ func versionRun(cmd *cobra.Command, _ []string) { cur = err.Error() } - if latest, err = latestVersion(false); err != nil { + if latest, err = LatestFiberVersion(); err != nil { _, _ = fmt.Fprintf(w, "fiber version: %v\n", err) return } @@ -36,13 +38,15 @@ func versionRun(cmd *cobra.Command, _ []string) { _, _ = fmt.Fprintf(w, "fiber version: %s (latest %s)\n", cur, latest) } -var currentVersionRegexp = regexp.MustCompile(`github\.com/gofiber/fiber[^\n]*? (.*)\n`) -var currentVersionFile = "go.mod" +var ( + currentVersionRegexp = regexp.MustCompile(`github\.com/gofiber/fiber[^\n]*? (.*)\n`) + currentVersionFile = "go.mod" +) func currentVersion() (string, error) { b, err := os.ReadFile(currentVersionFile) if err != nil { - return "", err + return "", fmt.Errorf("read current version file: %w", err) } if submatch := currentVersionRegexp.FindSubmatch(b); len(submatch) == 2 { @@ -54,20 +58,32 @@ func currentVersion() (string, error) { var latestVersionRegexp = regexp.MustCompile(`"name":\s*?"v(.*?)"`) -func latestVersion(isCli bool) (v string, err error) { +func LatestFiberVersion() (string, error) { + return latestVersionByURL("https://api.github.com/repos/gofiber/fiber/releases/latest") +} + +func LatestCliVersion() (string, error) { + return latestVersionByURL("https://api.github.com/repos/gofiber/cli/releases/latest") +} + +func latestVersionByURL(url string) (string, error) { var ( res *http.Response b []byte ) - if isCli { - res, err = http.Get("https://api.github.com/repos/gofiber/cli/releases/latest") - } else { - res, err = http.Get("https://api.github.com/repos/gofiber/fiber/releases/latest") + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + if err != nil { + return "", fmt.Errorf("create http request: %w", err) } + client := &http.Client{} + res, err = client.Do(req) if err != nil { - return "", err + return "", fmt.Errorf("http request failed: %w", err) } defer func() { @@ -76,8 +92,9 @@ func latestVersion(isCli bool) (v string, err error) { } }() - if b, err = io.ReadAll(res.Body); err != nil { - return "", err + b, err = io.ReadAll(res.Body) + if err != nil { + return "", fmt.Errorf("read response body: %w", err) } if submatch := latestVersionRegexp.FindSubmatch(b); len(submatch) == 2 { diff --git a/cmd/version_test.go b/cmd/version_test.go index f2c4cd7..b241372 100644 --- a/cmd/version_test.go +++ b/cmd/version_test.go @@ -8,6 +8,7 @@ import ( "github.com/jarcoal/httpmock" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func Test_Version_Printer(t *testing.T) { @@ -21,7 +22,7 @@ func Test_Version_Printer(t *testing.T) { httpmock.RegisterResponder(http.MethodGet, latestVersionURL, httpmock.NewBytesResponder(200, fakeVersionResponse)) out, err := runCobraCmd(versionCmd) - at.NoError(err) + require.NoError(t, err) at.Contains(out, "2.0.6") }) @@ -33,7 +34,7 @@ func Test_Version_Printer(t *testing.T) { httpmock.RegisterResponder(http.MethodGet, latestVersionURL, httpmock.NewBytesResponder(200, []byte("no version"))) out, err := runCobraCmd(versionCmd) - at.NoError(err) + require.NoError(t, err) at.Contains(out, "no version") }) } @@ -63,7 +64,7 @@ require ( defer teardownCurrentVersionFile() v, err := currentVersion() - at.NoError(err) + require.NoError(t, err) at.Equal("v2.0.6", v) }) @@ -79,7 +80,7 @@ require ( defer teardownCurrentVersionFile() v, err := currentVersion() - at.NoError(err) + require.NoError(t, err) at.Equal("v0.0.0-20200926082917-55763e7e6ee3", v) }) @@ -124,7 +125,7 @@ func Test_Version_Latest(t *testing.T) { httpmock.RegisterResponder(http.MethodGet, latestVersionURL, httpmock.NewErrorResponder(errors.New("network error"))) - _, err := latestVersion(false) + _, err := LatestFiberVersion() at.Error(err) }) @@ -135,8 +136,8 @@ func Test_Version_Latest(t *testing.T) { httpmock.RegisterResponder(http.MethodGet, latestVersionURL, httpmock.NewBytesResponder(200, fakeVersionResponse)) - v, err := latestVersion(false) - at.NoError(err) + v, err := LatestFiberVersion() + require.NoError(t, err) at.Equal("2.0.6", v) }) @@ -147,7 +148,7 @@ func Test_Version_Latest(t *testing.T) { httpmock.RegisterResponder(http.MethodGet, latestVersionURL, httpmock.NewBytesResponder(200, []byte("no version"))) - _, err := latestVersion(false) + _, err := LatestFiberVersion() at.Error(err) }) } From 9ee04a4271ea3145233d28320d1ddc6145add646 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9?= Date: Sat, 12 Jul 2025 21:46:42 +0200 Subject: [PATCH 14/27] fix lint issues --- cmd/internal/migrations/v3/common_test.go | 8 ++--- cmd/internal/prompt.go | 6 ++-- cmd/internal/task.go | 4 +-- cmd/migrate.go | 23 +++++++------ cmd/new.go | 42 ++++++++++++++--------- cmd/new_test.go | 2 +- cmd/root.go | 2 +- cmd/root_test.go | 2 +- cmd/tester_test.go | 2 +- 9 files changed, 52 insertions(+), 39 deletions(-) diff --git a/cmd/internal/migrations/v3/common_test.go b/cmd/internal/migrations/v3/common_test.go index 1267bd8..9c42c64 100644 --- a/cmd/internal/migrations/v3/common_test.go +++ b/cmd/internal/migrations/v3/common_test.go @@ -38,7 +38,7 @@ func Test_MigrateHandlerSignatures(t *testing.T) { dir, err := os.MkdirTemp("", "mhstest") require.NoError(t, err) - defer os.RemoveAll(dir) + defer func() { require.NoError(t, os.RemoveAll(dir)) }() file := writeTempFile(t, dir, "main.go", `package main import "github.com/gofiber/fiber/v2" @@ -60,7 +60,7 @@ func Test_MigrateParserMethods(t *testing.T) { dir, err := os.MkdirTemp("", "mptest") require.NoError(t, err) - defer os.RemoveAll(dir) + defer func() { require.NoError(t, os.RemoveAll(dir)) }() file := writeTempFile(t, dir, "main.go", `package main import "github.com/gofiber/fiber/v2" @@ -91,7 +91,7 @@ func Test_MigrateRedirectMethods(t *testing.T) { dir, err := os.MkdirTemp("", "mrtest") require.NoError(t, err) - defer os.RemoveAll(dir) + defer func() { require.NoError(t, os.RemoveAll(dir)) }() file := writeTempFile(t, dir, "main.go", `package main import "github.com/gofiber/fiber/v2" @@ -119,7 +119,7 @@ func Test_MigrateGenericHelpers(t *testing.T) { dir, err := os.MkdirTemp("", "mghtest") require.NoError(t, err) - defer os.RemoveAll(dir) + defer func() { require.NoError(t, os.RemoveAll(dir)) }() file := writeTempFile(t, dir, "main.go", `package main import "github.com/gofiber/fiber/v2" diff --git a/cmd/internal/prompt.go b/cmd/internal/prompt.go index d4e9352..f2ec59d 100644 --- a/cmd/internal/prompt.go +++ b/cmd/internal/prompt.go @@ -53,11 +53,11 @@ func parseBool(str string) bool { func (p *Prompt) Answer() (result string, err error) { if _, err = checkConsole(); err != nil { - return + return "", fmt.Errorf("check console: %w", err) } if _, err := p.p.Run(); err != nil { - return "", err + return "", fmt.Errorf("run prompt: %w", err) } return p.answer, nil } @@ -81,6 +81,8 @@ func (p *Prompt) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case tea.KeyEnter: p.answer = p.textInput.Value() return p, tea.Quit + default: + // ignore other keys } // We handle errors just like any other message diff --git a/cmd/internal/task.go b/cmd/internal/task.go index cf9e460..50a053e 100644 --- a/cmd/internal/task.go +++ b/cmd/internal/task.go @@ -45,7 +45,7 @@ func (t *SpinnerTask) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case tea.KeyMsg: switch msg.String() { case "q", "esc", "ctrl+c": - t.err = fmt.Errorf("quit by %s\n", msg.String()) + t.err = fmt.Errorf("quit by %s", msg.String()) return t, tea.Quit default: return t, nil @@ -81,7 +81,7 @@ func (t *SpinnerTask) Run() (err error) { } if _, err = t.p.Run(); err != nil { - return err + return fmt.Errorf("run spinner: %w", err) } return t.err diff --git a/cmd/migrate.go b/cmd/migrate.go index abd0411..a9508a9 100644 --- a/cmd/migrate.go +++ b/cmd/migrate.go @@ -11,15 +11,18 @@ import ( "github.com/spf13/cobra" ) -var ( - targetVersionS string -) +var targetVersionS string func init() { - latestFiberVersion, _ := LatestFiberVersion() + 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") - migrateCmd.MarkFlagRequired("to") + 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 { + panic(err) + } } var migrateCmd = &cobra.Command{ @@ -31,7 +34,7 @@ var migrateCmd = &cobra.Command{ func migrateRunE(cmd *cobra.Command, _ []string) error { currentVersionS, err := currentVersion() if err != nil { - return fmt.Errorf("current fiber project version not found: %v", err) + return fmt.Errorf("current fiber project version not found: %w", err) } currentVersionS = strings.TrimPrefix(currentVersionS, "v") currentVersion := semver.MustParse(currentVersionS) @@ -39,7 +42,7 @@ func migrateRunE(cmd *cobra.Command, _ []string) error { targetVersionS = strings.TrimPrefix(targetVersionS, "v") targetVersion, err := semver.NewVersion(targetVersionS) if err != nil { - return fmt.Errorf("invalid version for \"%s\": %v", targetVersionS, err) + return fmt.Errorf("invalid version for \"%s\": %w", targetVersionS, err) } if !targetVersion.GreaterThan(currentVersion) { @@ -48,12 +51,12 @@ func migrateRunE(cmd *cobra.Command, _ []string) error { wd, err := os.Getwd() if err != nil { - return fmt.Errorf("cannot get current working directory: %v", err) + return fmt.Errorf("cannot get current working directory: %w", err) } err = migrations.DoMigration(cmd, wd, currentVersion, targetVersion) if err != nil { - return fmt.Errorf("migration failed %v", err) + return fmt.Errorf("migration failed %w", err) } msg := fmt.Sprintf("Migration from Fiber %s to %s", currentVersionS, targetVersionS) diff --git a/cmd/new.go b/cmd/new.go index 89a03cd..8de0105 100644 --- a/cmd/new.go +++ b/cmd/new.go @@ -37,15 +37,20 @@ func newRunE(cmd *cobra.Command, args []string) (err error) { modName = args[1] } - wd, _ := os.Getwd() + wd, err := os.Getwd() + if err != nil { + return fmt.Errorf("getwd: %w", err) + } projectPath := fmt.Sprintf("%s%c%s", wd, os.PathSeparator, projectName) - if err = createProject(projectPath); err != nil { + if err := createProject(projectPath); err != nil { return err } defer func() { if err != nil { - _ = os.RemoveAll(projectPath) + if rmErr := os.RemoveAll(projectPath); rmErr != nil { + fmt.Fprintf(cmd.ErrOrStderr(), "failed to remove project dir: %v", rmErr) + } } }() @@ -64,17 +69,20 @@ func newRunE(cmd *cobra.Command, args []string) (err error) { return create(projectPath, modName) } -func createProject(projectPath string) (err error) { - if err = os.Mkdir(projectPath, 0o750); err != nil { - return err +func createProject(projectPath string) error { + if err := os.Mkdir(projectPath, 0o750); err != nil { + return fmt.Errorf("create directory: %w", err) + } + + if err := os.Chdir(projectPath); err != nil { + return fmt.Errorf("change directory: %w", err) } - return os.Chdir(projectPath) + return nil } -func createBasic(projectPath, modName string) (err error) { - // create main.go - if err = createFile(fmt.Sprintf("%s%cmain.go", projectPath, os.PathSeparator), newBasicTemplate); err != nil { +func createBasic(projectPath, modName string) error { + if err := createFile(fmt.Sprintf("%s%cmain.go", projectPath, os.PathSeparator), newBasicTemplate); err != nil { return err } @@ -88,9 +96,9 @@ const ( var fullPathRegex = regexp.MustCompile(`^(http|https|git)`) -func createComplex(projectPath, modName string) (err error) { - var git string - if git, err = execLookPath("git"); err != nil { +func createComplex(projectPath, modName string) error { + git, err := execLookPath("git") + if err != nil { return err } @@ -99,20 +107,20 @@ func createComplex(projectPath, modName string) (err error) { toClone = repo } - if err = runCmd(execCommand(git, "clone", toClone, projectPath)); err != nil { + if err := runCmd(execCommand(git, "clone", toClone, projectPath)); err != nil { return err } if repo == defaultRepo { - if err = replace(projectPath, "go.mod", "boilerplate", modName); err != nil { + if err := replace(projectPath, "go.mod", "boilerplate", modName); err != nil { return err } - if err = replace(projectPath, "*.go", "boilerplate", modName); err != nil { + if err := replace(projectPath, "*.go", "boilerplate", modName); err != nil { return err } } - return err + return nil } var ( diff --git a/cmd/new_test.go b/cmd/new_test.go index 0554023..977b3ec 100644 --- a/cmd/new_test.go +++ b/cmd/new_test.go @@ -14,7 +14,7 @@ func Test_New_Run(t *testing.T) { t.Run("new project", func(t *testing.T) { defer func() { require.NoError(t, os.Chdir("../")) - _ = os.RemoveAll("normal") + require.NoError(t, os.RemoveAll("normal")) }() setupCmd() diff --git a/cmd/root.go b/cmd/root.go index 0e9145d..f0d5c7e 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -49,7 +49,7 @@ func Execute() { } func rootRunE(cmd *cobra.Command, _ []string) error { - return cmd.Help() + return fmt.Errorf("help: %w", cmd.Help()) } func rootPersistentPreRun(cmd *cobra.Command, _ []string) { diff --git a/cmd/root_test.go b/cmd/root_test.go index 6bc1f8a..7527826 100644 --- a/cmd/root_test.go +++ b/cmd/root_test.go @@ -23,7 +23,7 @@ func Test_Root_Execute(t *testing.T) { oldRunE := rootCmd.RunE rootCmd.RunE = func(_ *cobra.Command, _ []string) error { - return fmt.Errorf("fake error") + return errors.New("fake error") } Execute() diff --git a/cmd/tester_test.go b/cmd/tester_test.go index e7b96fe..c36bef4 100644 --- a/cmd/tester_test.go +++ b/cmd/tester_test.go @@ -22,7 +22,7 @@ var ( func fakeExecCommand(command string, args ...string) *exec.Cmd { cs := []string{"-test.run=TestHelperProcess", "--", command} cs = append(cs, args...) - // gosec: G204 - safe for test, args are controlled + // #nosec G204 -- safe for test, args are controlled cmd := exec.Command(os.Args[0], cs...) cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"} if needError { From 49be48961044bfbf2589eac272bed55830be223e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9?= Date: Sat, 12 Jul 2025 22:02:43 +0200 Subject: [PATCH 15/27] fix lint issues --- cmd/helpers.go | 99 +++++++++++++++-------- cmd/helpers_test.go | 5 +- cmd/internal/cmd.go | 45 ++++++----- cmd/internal/helpers.go | 25 +++--- cmd/internal/migrations/common.go | 8 +- cmd/internal/migrations/lists.go | 6 +- cmd/internal/migrations/v3/common.go | 8 +- cmd/internal/migrations/v3/common_test.go | 4 +- cmd/internal/task.go | 4 +- cmd/root.go | 4 +- 10 files changed, 120 insertions(+), 88 deletions(-) diff --git a/cmd/helpers.go b/cmd/helpers.go index 931eacd..6b98bf6 100644 --- a/cmd/helpers.go +++ b/cmd/helpers.go @@ -23,7 +23,9 @@ var ( ) func init() { - homeDir, _ = os.UserHomeDir() + if dir, err := os.UserHomeDir(); err == nil { + homeDir = dir + } } func runCmd(cmd *exec.Cmd) (err error) { @@ -33,74 +35,96 @@ func runCmd(cmd *exec.Cmd) (err error) { ) if stderr, err = cmd.StderrPipe(); err != nil { - return err + return fmt.Errorf("stderr pipe: %w", err) } defer func() { - _ = stderr.Close() + if cerr := stderr.Close(); cerr != nil { + fmt.Fprintf(os.Stderr, "close stderr pipe: %v", cerr) + } + }() + go func() { + if _, cErr := io.Copy(os.Stderr, stderr); cErr != nil { + fmt.Fprintf(os.Stderr, "copy stderr: %v", cErr) + } }() - go func() { _, _ = io.Copy(os.Stderr, stderr) }() if stdout, err = cmd.StdoutPipe(); err != nil { - return err + return fmt.Errorf("stdout pipe: %w", err) } defer func() { - _ = stdout.Close() + if cerr := stdout.Close(); cerr != nil { + fmt.Fprintf(os.Stderr, "close stdout pipe: %v", cerr) + } + }() + go func() { + if _, cErr := io.Copy(os.Stdout, stdout); cErr != nil { + fmt.Fprintf(os.Stderr, "copy stdout: %v", cErr) + } }() - go func() { _, _ = io.Copy(os.Stdout, stdout) }() if err = cmd.Run(); err != nil { - err = fmt.Errorf("failed to run %s", cmd.String()) + err = fmt.Errorf("failed to run %s: %w", cmd.String(), err) } return err } // replaces matching file patterns in a path, including subdirectories -func replace(path, pattern, old, new string) error { - return filepath.Walk(path, func(path string, info os.FileInfo, err error) error { +func replace(pathname, pattern, old, replacement string) error { + walkErr := filepath.Walk(pathname, func(p string, info os.FileInfo, err error) error { if err != nil { return err } if info.IsDir() { return nil } - return replaceWalkFn(path, info, pattern, []byte(old), []byte(new)) + return replaceWalkFn(p, info, pattern, []byte(old), []byte(replacement)) }) + if walkErr != nil { + return fmt.Errorf("walk %s: %w", pathname, walkErr) + } + return nil } -func replaceWalkFn(path string, info os.FileInfo, pattern string, old, new []byte) (err error) { +func replaceWalkFn(pathname string, info os.FileInfo, pattern string, old, replacement []byte) (err error) { var matched bool if matched, err = filepath.Match(pattern, info.Name()); err != nil { - return err + return fmt.Errorf("match pattern %s: %w", pattern, err) } if matched { - cleanedPath := filepath.Clean(path) + cleanedPath := filepath.Clean(pathname) - var oldContent []byte - if oldContent, err = os.ReadFile(cleanedPath); err != nil { - return err + oldContent, readErr := os.ReadFile(cleanedPath) + if readErr != nil { + return fmt.Errorf("read file %s: %w", cleanedPath, readErr) } - if err = os.WriteFile(cleanedPath, bytes.Replace(oldContent, old, new, -1), 0); err != nil { - return err + if err := os.WriteFile(cleanedPath, bytes.Replace(oldContent, old, replacement, -1), 0); err != nil { + return fmt.Errorf("write file %s: %w", cleanedPath, err) } } return nil } -func createFile(filePath, content string) (err error) { - var f *os.File - if f, err = os.Create(filepath.Clean(filePath)); err != nil { - return err +func createFile(filePath, content string) error { + f, err := os.Create(filepath.Clean(filePath)) + if err != nil { + return fmt.Errorf("create %s: %w", filePath, err) } - defer func() { _ = f.Close() }() + defer func() { + if cerr := f.Close(); cerr != nil { + fmt.Fprintf(os.Stderr, "close file: %v", cerr) + } + }() - _, err = f.WriteString(content) + if _, err := f.WriteString(content); err != nil { + return fmt.Errorf("write %s: %w", filePath, err) + } - return err + return nil } func formatLatency(d time.Duration) time.Duration { @@ -120,7 +144,7 @@ func loadConfig() (err error) { configFilePath := configFilePath() if fileExist(configFilePath) { - if err = loadJson(configFilePath, &rc); err != nil { + if err := loadJSON(configFilePath, &rc); err != nil { return err } } @@ -128,8 +152,8 @@ func loadConfig() (err error) { return nil } -func storeConfig() { - _ = storeJSON(configFilePath(), rc) +func storeConfig() error { + return storeJSON(configFilePath(), rc) } func configFilePath() string { @@ -150,17 +174,24 @@ var fileExist = func(filename string) bool { func storeJSON(filename string, v any) error { b, err := json.MarshalIndent(v, "", " ") if err != nil { - return err + return fmt.Errorf("marshal json: %w", err) } - return os.WriteFile(filename, b, 0o600) + if err := os.WriteFile(filename, b, 0o600); err != nil { + return fmt.Errorf("write %s: %w", filename, err) + } + + return nil } -func loadJson(filename string, v any) error { +func loadJSON(filename string, v any) error { b, err := os.ReadFile(path.Clean(filename)) if err != nil { - return err + return fmt.Errorf("read file %s: %w", filename, err) } - return json.Unmarshal(b, v) + if err := json.Unmarshal(b, v); err != nil { + return fmt.Errorf("unmarshal %s: %w", filename, err) + } + return nil } diff --git a/cmd/helpers_test.go b/cmd/helpers_test.go index c7c9553..ade4d90 100644 --- a/cmd/helpers_test.go +++ b/cmd/helpers_test.go @@ -3,6 +3,7 @@ package cmd import ( "fmt" "os" + "path/filepath" "testing" "time" @@ -29,7 +30,7 @@ func Test_Helpers_FormatLatency(t *testing.T) { for _, tc := range cases { t.Run(tc.d.String(), func(t *testing.T) { t.Parallel() - assert.Equal(t, formatLatency(tc.d), tc.expected) + assert.Equal(t, tc.expected, formatLatency(tc.d)) }) } } @@ -70,7 +71,7 @@ func Test_Helper_LoadConfig(t *testing.T) { filename := fmt.Sprintf("%s%c%s", homeDir, os.PathSeparator, configName) - f, err := os.Create(filename) + f, err := os.Create(filepath.Clean(filename)) require.NoError(t, err) defer func() { require.NoError(t, f.Close()) }() _, err = f.WriteString("{}") diff --git a/cmd/internal/cmd.go b/cmd/internal/cmd.go index 5a10aec..cf00f6f 100644 --- a/cmd/internal/cmd.go +++ b/cmd/internal/cmd.go @@ -29,7 +29,7 @@ type SpinnerCmd struct { } func NewSpinnerCmd(cmd *exec.Cmd, title ...string) *SpinnerCmd { - spinnerModel := spinner.NewModel() + spinnerModel := spinner.New() spinnerModel.Spinner = spinner.Dot c := &SpinnerCmd{ @@ -51,37 +51,38 @@ func NewSpinnerCmd(cmd *exec.Cmd, title ...string) *SpinnerCmd { } func (t *SpinnerCmd) Init() tea.Cmd { - return tea.Batch(t.init(), spinner.Tick) + return tea.Batch(t.start(), t.spinnerModel.Tick) } -func (t *SpinnerCmd) init() tea.Cmd { +func (t *SpinnerCmd) start() tea.Cmd { return func() tea.Msg { - if p, err := t.cmd.StdoutPipe(); err != nil { - return finishedMsg{err} - } else { - go t.watchOutput(t.stdout, p) + p, err := t.cmd.StdoutPipe() + if err != nil { + return finishedError{err} } - if p, err := t.cmd.StderrPipe(); err != nil { - return finishedMsg{err} - } else { - go t.watchOutput(t.stderr, p) + go t.watchOutput(t.stdout, p) + + p, err = t.cmd.StderrPipe() + if err != nil { + return finishedError{err} } - return finishedMsg{t.cmd.Start()} + go t.watchOutput(t.stderr, p) + + return finishedError{t.cmd.Start()} } } func (t *SpinnerCmd) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { - case tea.KeyMsg: switch msg.String() { case "q", "esc", "ctrl+c": - t.err = fmt.Errorf("quit by %s\n", msg.String()) + t.err = fmt.Errorf("quit by %s", msg.String()) return t, tea.Quit default: return t, nil } - case finishedMsg: + case finishedError: if t.err = msg.error; t.err != nil { return t, tea.Quit } @@ -132,16 +133,16 @@ const spinnerCmdTemplate = ` %s %s %s (esc/q/ctrl+c to quit) - + ` func (t *SpinnerCmd) Run() (err error) { if t.size, err = checkConsole(); err != nil { - return + return err } - if err = t.p.Start(); err != nil { - return + if _, err = t.p.Run(); err != nil { + return err } return t.err @@ -158,7 +159,11 @@ func (t *SpinnerCmd) UpdateOutput(c <-chan []byte) { } func (t *SpinnerCmd) watchOutput(out chan<- []byte, rc io.ReadCloser) { - defer func() { _ = rc.Close() }() + defer func() { + if err := rc.Close(); err != nil { + t.errCh <- err + } + }() br := bufio.NewReader(rc) for { b, _, err := br.ReadLine() diff --git a/cmd/internal/helpers.go b/cmd/internal/helpers.go index 2eb75ae..770d598 100644 --- a/cmd/internal/helpers.go +++ b/cmd/internal/helpers.go @@ -6,14 +6,13 @@ import ( "path/filepath" "strings" - tea "github.com/charmbracelet/bubbletea" "github.com/containerd/console" "github.com/muesli/termenv" ) var term = termenv.ColorProfile() -type finishedMsg struct{ error } +type finishedError struct{ error } func checkConsole() (size console.WinSize, err error) { defer func() { @@ -22,28 +21,23 @@ func checkConsole() (size console.WinSize, err error) { } }() - return console.Current().Size() -} - -func errCmd(err error) tea.Cmd { - return func() tea.Msg { - return finishedMsg{err} + size, err = console.Current().Size() + if err != nil { + return size, fmt.Errorf("get console size: %w", err) } + return size, nil } type FileProcessor func(content string) string func ChangeFileContent(cwd string, processorFn FileProcessor) error { - // change go files in project err := filepath.Walk(cwd, func(path string, info os.FileInfo, err error) error { if err != nil { - // fmt.Printf("Error while traversing %s: %v\n", path, err) return err } // Skip directories named "vendor" if info.IsDir() && info.Name() == "vendor" { - // fmt.Printf("Skipping directory: %s\n", path) return filepath.SkipDir } @@ -51,18 +45,17 @@ func ChangeFileContent(cwd string, processorFn FileProcessor) error { if info.IsDir() || !strings.HasSuffix(info.Name(), ".go") { return nil } - // fmt.Printf("Processing Go file: %s\n", path) - fileContent, err := os.ReadFile(path) + fileContent, err := os.ReadFile(path) // #nosec G304 // update go.mod file - if err2 := os.WriteFile(path, []byte(processorFn(string(fileContent))), 0o644); err != nil { - return err2 + if err2 := os.WriteFile(path, []byte(processorFn(string(fileContent))), 0o600); err2 != nil { + return fmt.Errorf("write file %s: %w", path, err2) } return nil }) if err != nil { - return fmt.Errorf("Error while traversing the directory tree: %v\n", err) + return fmt.Errorf("error while traversing the directory tree: %w", err) } return nil diff --git a/cmd/internal/migrations/common.go b/cmd/internal/migrations/common.go index 6893958..d4d2124 100644 --- a/cmd/internal/migrations/common.go +++ b/cmd/internal/migrations/common.go @@ -25,14 +25,14 @@ func MigrateGoPkgs(cmd *cobra.Command, cwd string, curr *semver.Version, target return pkgReplacer.Replace(content) }) if err != nil { - return fmt.Errorf("failed to migrate Go packages: %v", err) + return fmt.Errorf("failed to migrate Go packages: %w", err) } // get go.mod file modFile := "go.mod" fileContent, err := os.ReadFile(modFile) if err != nil { - return err + return fmt.Errorf("read %s: %w", modFile, err) } // replace old version with new version in go.mod file @@ -42,8 +42,8 @@ func MigrateGoPkgs(cmd *cobra.Command, cwd string, curr *semver.Version, target ) // update go.mod file - if err := os.WriteFile(modFile, []byte(fileContentStr), 0o644); err != nil { - return err + if err := os.WriteFile(modFile, []byte(fileContentStr), 0o600); err != nil { + return fmt.Errorf("write %s: %w", modFile, err) } cmd.Println("Migrating Go packages") diff --git a/cmd/internal/migrations/lists.go b/cmd/internal/migrations/lists.go index 62fd9fe..d5c4e35 100644 --- a/cmd/internal/migrations/lists.go +++ b/cmd/internal/migrations/lists.go @@ -42,11 +42,11 @@ func DoMigration(cmd *cobra.Command, cwd string, curr *semver.Version, target *s for _, m := range Migrations { toC, err := semver.NewConstraint(m.To) if err != nil { - return err + return fmt.Errorf("parse to constraint %s: %w", m.To, err) } fromC, err := semver.NewConstraint(m.From) if err != nil { - return err + return fmt.Errorf("parse from constraint %s: %w", m.From, err) } if fromC.Check(curr) && toC.Check(target) { @@ -56,7 +56,7 @@ func DoMigration(cmd *cobra.Command, cwd string, curr *semver.Version, target *s } } } else { - fmt.Printf("Skipping migration from %s to %s\n", m.From, m.To) + _, _ = fmt.Printf("Skipping migration from %s to %s\n", m.From, m.To) } } diff --git a/cmd/internal/migrations/v3/common.go b/cmd/internal/migrations/v3/common.go index d66cd62..e7465e8 100644 --- a/cmd/internal/migrations/v3/common.go +++ b/cmd/internal/migrations/v3/common.go @@ -18,7 +18,7 @@ func MigrateHandlerSignatures(cmd *cobra.Command, cwd string, _ *semver.Version, return sigReplacer.Replace(content) }) if err != nil { - return fmt.Errorf("failed to migrate handler signatures: %v", err) + return fmt.Errorf("failed to migrate handler signatures: %w", err) } cmd.Println("Migrating handler signatures") @@ -39,7 +39,7 @@ func MigrateParserMethods(cmd *cobra.Command, cwd string, _ *semver.Version, _ * return replacer.Replace(content) }) if err != nil { - return fmt.Errorf("failed to migrate parser methods: %v", err) + return fmt.Errorf("failed to migrate parser methods: %w", err) } cmd.Println("Migrating parser methods") @@ -59,7 +59,7 @@ func MigrateRedirectMethods(cmd *cobra.Command, cwd string, _ *semver.Version, _ return replacer.Replace(content) }) if err != nil { - return fmt.Errorf("failed to migrate redirect methods: %v", err) + return fmt.Errorf("failed to migrate redirect methods: %w", err) } cmd.Println("Migrating redirect methods") @@ -84,7 +84,7 @@ func MigrateGenericHelpers(cmd *cobra.Command, cwd string, _ *semver.Version, _ return content }) if err != nil { - return fmt.Errorf("failed to migrate generic helpers: %v", err) + return fmt.Errorf("failed to migrate generic helpers: %w", err) } cmd.Println("Migrating generic helpers") diff --git a/cmd/internal/migrations/v3/common_test.go b/cmd/internal/migrations/v3/common_test.go index 9c42c64..27c361c 100644 --- a/cmd/internal/migrations/v3/common_test.go +++ b/cmd/internal/migrations/v3/common_test.go @@ -14,14 +14,14 @@ import ( func writeTempFile(t *testing.T, dir, name, content string) string { t.Helper() path := filepath.Join(dir, name) - err := os.WriteFile(path, []byte(content), 0o644) + 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) + b, err := os.ReadFile(path) // #nosec G304 require.NoError(t, err) return string(b) } diff --git a/cmd/internal/task.go b/cmd/internal/task.go index 50a053e..2ac90b3 100644 --- a/cmd/internal/task.go +++ b/cmd/internal/task.go @@ -36,7 +36,7 @@ func NewSpinnerTask(title string, task Task) *SpinnerTask { func (t *SpinnerTask) Init() tea.Cmd { return tea.Batch( func() tea.Msg { - return finishedMsg{t.task()} + return finishedError{t.task()} }, t.spinnerModel.Tick) } @@ -51,7 +51,7 @@ func (t *SpinnerTask) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return t, nil } - case finishedMsg: + case finishedError: t.err = msg.error return t, tea.Quit diff --git a/cmd/root.go b/cmd/root.go index f0d5c7e..bf06cc1 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -95,7 +95,9 @@ func checkCliVersion(cmd *cobra.Command) { func updateVersionCheckedAt() { rc.CliVersionCheckedAt = time.Now().Unix() - storeConfig() + if err := storeConfig(); err != nil { + fmt.Printf("failed to store config: %v\n", err) + } } func needCheckCliVersion() bool { From 9a0b3e6a86b519451f3a74f88f9aa5c18df88cc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9?= Date: Sat, 12 Jul 2025 22:22:06 +0200 Subject: [PATCH 16/27] fix lint issues --- cmd/helpers.go | 2 +- cmd/internal/cmd.go | 2 +- cmd/internal/helpers.go | 3 +++ cmd/internal/migrations/common.go | 2 +- cmd/internal/migrations/lists.go | 16 ++++++++-------- cmd/internal/migrations/v3/common.go | 8 ++++---- cmd/internal/migrations/v3/common_test.go | 12 ++++++------ cmd/root.go | 5 ++++- 8 files changed, 28 insertions(+), 22 deletions(-) diff --git a/cmd/helpers.go b/cmd/helpers.go index 6b98bf6..b3b3412 100644 --- a/cmd/helpers.go +++ b/cmd/helpers.go @@ -100,7 +100,7 @@ func replaceWalkFn(pathname string, info os.FileInfo, pattern string, old, repla return fmt.Errorf("read file %s: %w", cleanedPath, readErr) } - if err := os.WriteFile(cleanedPath, bytes.Replace(oldContent, old, replacement, -1), 0); err != nil { + if err := os.WriteFile(cleanedPath, bytes.ReplaceAll(oldContent, old, replacement), 0); err != nil { return fmt.Errorf("write file %s: %w", cleanedPath, err) } } diff --git a/cmd/internal/cmd.go b/cmd/internal/cmd.go index cf00f6f..e82db40 100644 --- a/cmd/internal/cmd.go +++ b/cmd/internal/cmd.go @@ -142,7 +142,7 @@ func (t *SpinnerCmd) Run() (err error) { } if _, err = t.p.Run(); err != nil { - return err + return fmt.Errorf("program run: %w", err) } return t.err diff --git a/cmd/internal/helpers.go b/cmd/internal/helpers.go index 770d598..1a17874 100644 --- a/cmd/internal/helpers.go +++ b/cmd/internal/helpers.go @@ -46,6 +46,9 @@ func ChangeFileContent(cwd string, processorFn FileProcessor) error { return nil } fileContent, err := os.ReadFile(path) // #nosec G304 + if err != nil { + return fmt.Errorf("read file %s: %w", path, err) + } // update go.mod file if err2 := os.WriteFile(path, []byte(processorFn(string(fileContent))), 0o600); err2 != nil { diff --git a/cmd/internal/migrations/common.go b/cmd/internal/migrations/common.go index d4d2124..b24b35d 100644 --- a/cmd/internal/migrations/common.go +++ b/cmd/internal/migrations/common.go @@ -15,7 +15,7 @@ import ( var pkgRegex = regexp.MustCompile(`(github\.com\/gofiber\/fiber\/)(v\d+)( *?)(v[\w.-]+)`) -func MigrateGoPkgs(cmd *cobra.Command, cwd string, curr *semver.Version, target *semver.Version) error { +func MigrateGoPkgs(cmd *cobra.Command, cwd string, curr, target *semver.Version) error { pkgReplacer := strings.NewReplacer( "github.com/gofiber/fiber/v"+strconv.FormatUint(curr.Major(), 10), "github.com/gofiber/fiber/v"+strconv.FormatUint(target.Major(), 10), diff --git a/cmd/internal/migrations/lists.go b/cmd/internal/migrations/lists.go index d5c4e35..a543a09 100644 --- a/cmd/internal/migrations/lists.go +++ b/cmd/internal/migrations/lists.go @@ -6,11 +6,11 @@ import ( semver "github.com/Masterminds/semver/v3" "github.com/spf13/cobra" - "github.com/gofiber/cli/cmd/internal/migrations/v3" + v3migrations "github.com/gofiber/cli/cmd/internal/migrations/v3" ) // MigrationFn is a function that will be called during migration -type MigrationFn func(cmd *cobra.Command, cwd string, curr *semver.Version, target *semver.Version) error +type MigrationFn func(cmd *cobra.Command, cwd string, curr, target *semver.Version) error // Migration is a single migration type Migration struct { @@ -27,10 +27,10 @@ var Migrations = []Migration{ From: ">=2.0.0", To: "<4.0.0-0", Functions: []MigrationFn{ - v3.MigrateHandlerSignatures, - v3.MigrateParserMethods, - v3.MigrateRedirectMethods, - v3.MigrateGenericHelpers, + v3migrations.MigrateHandlerSignatures, + v3migrations.MigrateParserMethods, + v3migrations.MigrateRedirectMethods, + v3migrations.MigrateGenericHelpers, }, }, {From: ">=1.0.0", To: ">=0.0.0-0", Functions: []MigrationFn{MigrateGoPkgs}}, @@ -38,7 +38,7 @@ var Migrations = []Migration{ // DoMigration runs all migrations // It will run all migrations that match the current and target version -func DoMigration(cmd *cobra.Command, cwd string, curr *semver.Version, target *semver.Version) error { +func DoMigration(cmd *cobra.Command, cwd string, curr, target *semver.Version) error { for _, m := range Migrations { toC, err := semver.NewConstraint(m.To) if err != nil { @@ -56,7 +56,7 @@ func DoMigration(cmd *cobra.Command, cwd string, curr *semver.Version, target *s } } } else { - _, _ = fmt.Printf("Skipping migration from %s to %s\n", m.From, m.To) + cmd.Printf("Skipping migration from %s to %s\n", m.From, m.To) } } diff --git a/cmd/internal/migrations/v3/common.go b/cmd/internal/migrations/v3/common.go index e7465e8..bc73d1a 100644 --- a/cmd/internal/migrations/v3/common.go +++ b/cmd/internal/migrations/v3/common.go @@ -11,7 +11,7 @@ import ( "github.com/gofiber/cli/cmd/internal" ) -func MigrateHandlerSignatures(cmd *cobra.Command, cwd string, _ *semver.Version, _ *semver.Version) error { +func MigrateHandlerSignatures(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { sigReplacer := strings.NewReplacer("*fiber.Ctx", "fiber.Ctx") err := internal.ChangeFileContent(cwd, func(content string) string { @@ -27,7 +27,7 @@ func MigrateHandlerSignatures(cmd *cobra.Command, cwd string, _ *semver.Version, } // MigrateParserMethods replaces deprecated parser helper methods with the new binding API -func MigrateParserMethods(cmd *cobra.Command, cwd string, _ *semver.Version, _ *semver.Version) error { +func MigrateParserMethods(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { replacer := strings.NewReplacer( ".BodyParser(", ".Bind().Body(", ".CookieParser(", ".Bind().Cookie(", @@ -47,7 +47,7 @@ func MigrateParserMethods(cmd *cobra.Command, cwd string, _ *semver.Version, _ * } // MigrateRedirectMethods updates redirect helper methods to the new API -func MigrateRedirectMethods(cmd *cobra.Command, cwd string, _ *semver.Version, _ *semver.Version) error { +func MigrateRedirectMethods(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { replacer := strings.NewReplacer( ".RedirectBack(", ".Redirect().Back(", ".RedirectToRoute(", ".Redirect().Route(", @@ -67,7 +67,7 @@ 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, _ *semver.Version) error { +func MigrateGenericHelpers(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { err := internal.ChangeFileContent(cwd, func(content string) string { reParamsInt := regexp.MustCompile(`(\w+)\.ParamsInt\(`) content = reParamsInt.ReplaceAllString(content, "fiber.Params[int]($1, ") diff --git a/cmd/internal/migrations/v3/common_test.go b/cmd/internal/migrations/v3/common_test.go index 27c361c..eb3e049 100644 --- a/cmd/internal/migrations/v3/common_test.go +++ b/cmd/internal/migrations/v3/common_test.go @@ -11,9 +11,9 @@ import ( "github.com/stretchr/testify/require" ) -func writeTempFile(t *testing.T, dir, name, content string) string { +func writeTempFile(t *testing.T, dir, content string) string { t.Helper() - path := filepath.Join(dir, name) + path := filepath.Join(dir, "main.go") err := os.WriteFile(path, []byte(content), 0o600) require.NoError(t, err) return path @@ -40,7 +40,7 @@ func Test_MigrateHandlerSignatures(t *testing.T) { require.NoError(t, err) defer func() { require.NoError(t, os.RemoveAll(dir)) }() - file := writeTempFile(t, dir, "main.go", `package main + file := writeTempFile(t, dir, `package main import "github.com/gofiber/fiber/v2" func handler(c *fiber.Ctx) error { return nil } `) @@ -62,7 +62,7 @@ func Test_MigrateParserMethods(t *testing.T) { require.NoError(t, err) defer func() { require.NoError(t, os.RemoveAll(dir)) }() - file := writeTempFile(t, dir, "main.go", `package main + file := writeTempFile(t, dir, `package main import "github.com/gofiber/fiber/v2" func handler(c fiber.Ctx) error { var v any @@ -93,7 +93,7 @@ func Test_MigrateRedirectMethods(t *testing.T) { require.NoError(t, err) defer func() { require.NoError(t, os.RemoveAll(dir)) }() - file := writeTempFile(t, dir, "main.go", `package main + file := writeTempFile(t, dir, `package main import "github.com/gofiber/fiber/v2" func handler(c fiber.Ctx) error { c.Redirect("/foo") @@ -121,7 +121,7 @@ func Test_MigrateGenericHelpers(t *testing.T) { require.NoError(t, err) defer func() { require.NoError(t, os.RemoveAll(dir)) }() - file := writeTempFile(t, dir, "main.go", `package main + file := writeTempFile(t, dir, `package main import "github.com/gofiber/fiber/v2" func handler(c fiber.Ctx) error { _ = c.ParamsInt("id", 0) diff --git a/cmd/root.go b/cmd/root.go index bf06cc1..f1da5d3 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -2,6 +2,7 @@ package cmd import ( "fmt" + "os" "time" "github.com/gofiber/cli/cmd/internal" @@ -96,7 +97,9 @@ func checkCliVersion(cmd *cobra.Command) { func updateVersionCheckedAt() { rc.CliVersionCheckedAt = time.Now().Unix() if err := storeConfig(); err != nil { - fmt.Printf("failed to store config: %v\n", err) + if _, pErr := fmt.Fprintf(os.Stdout, "failed to store config: %v\n", err); pErr != nil { + fmt.Fprintf(os.Stderr, "print error: %v", pErr) + } } } From fe909554e68406d042fab12d87669ccd638b2c51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9?= Date: Sat, 12 Jul 2025 22:24:12 +0200 Subject: [PATCH 17/27] fix tests --- cmd/helpers_test.go | 5 ----- cmd/root_test.go | 3 ++- cmd/upgrade_test.go | 2 -- cmd/version_test.go | 10 ---------- 4 files changed, 2 insertions(+), 18 deletions(-) diff --git a/cmd/helpers_test.go b/cmd/helpers_test.go index ade4d90..9f3d164 100644 --- a/cmd/helpers_test.go +++ b/cmd/helpers_test.go @@ -52,15 +52,11 @@ func Test_Helper_Replace(t *testing.T) { } func Test_Helper_LoadConfig(t *testing.T) { - t.Parallel() - t.Run("no config file", func(t *testing.T) { - t.Parallel() require.NoError(t, loadConfig()) }) t.Run("has config file", func(t *testing.T) { - t.Parallel() origHome := homeDir tempHome := setupHomeDir(t, "LoadConfig") homeDir = tempHome @@ -88,7 +84,6 @@ func Test_Helper_StoreJSON(t *testing.T) { } func Test_Helper_ConfigFilePath(t *testing.T) { - t.Parallel() dir := homeDir homeDir = "" assert.Equal(t, configName, configFilePath()) diff --git a/cmd/root_test.go b/cmd/root_test.go index 7527826..7c38b91 100644 --- a/cmd/root_test.go +++ b/cmd/root_test.go @@ -36,7 +36,8 @@ func Test_Root_Execute(t *testing.T) { func Test_Root_RunE(t *testing.T) { at, b := setupRootCmd(t) - require.NoError(t, rootRunE(rootCmd, nil)) + err := rootRunE(rootCmd, nil) + require.Error(t, err) at.Contains(b.String(), "fiber") } diff --git a/cmd/upgrade_test.go b/cmd/upgrade_test.go index 1c6c16e..ac35b2e 100644 --- a/cmd/upgrade_test.go +++ b/cmd/upgrade_test.go @@ -12,7 +12,6 @@ import ( ) func Test_Upgrade_upgradeRunE(t *testing.T) { - t.Parallel() at := assert.New(t) b := &bytes.Buffer{} @@ -45,7 +44,6 @@ func Test_Upgrade_upgradeRunE(t *testing.T) { } func Test_Upgrade_upgrade(t *testing.T) { - t.Parallel() at := assert.New(t) b := &bytes.Buffer{} diff --git a/cmd/version_test.go b/cmd/version_test.go index b241372..a2d502c 100644 --- a/cmd/version_test.go +++ b/cmd/version_test.go @@ -12,10 +12,8 @@ import ( ) func Test_Version_Printer(t *testing.T) { - t.Parallel() at := assert.New(t) t.Run("success", func(t *testing.T) { - t.Parallel() httpmock.Activate() defer httpmock.DeactivateAndReset() @@ -27,7 +25,6 @@ func Test_Version_Printer(t *testing.T) { }) t.Run("latest err", func(t *testing.T) { - t.Parallel() httpmock.Activate() defer httpmock.DeactivateAndReset() @@ -40,11 +37,9 @@ func Test_Version_Printer(t *testing.T) { } func Test_Version_Current(t *testing.T) { - t.Parallel() at := assert.New(t) t.Run("file not found", func(t *testing.T) { - t.Parallel() setupCurrentVersionFile() defer teardownCurrentVersionFile() @@ -85,7 +80,6 @@ require ( }) t.Run("package not found", func(t *testing.T) { - t.Parallel() content := `module fiber-demo go 1.14 require ( @@ -116,10 +110,8 @@ func teardownCurrentVersionFile() { } func Test_Version_Latest(t *testing.T) { - t.Parallel() at := assert.New(t) t.Run("http get error", func(t *testing.T) { - t.Parallel() httpmock.Activate() defer httpmock.DeactivateAndReset() @@ -130,7 +122,6 @@ func Test_Version_Latest(t *testing.T) { }) t.Run("version matched", func(t *testing.T) { - t.Parallel() httpmock.Activate() defer httpmock.DeactivateAndReset() @@ -142,7 +133,6 @@ func Test_Version_Latest(t *testing.T) { }) t.Run("no version matched", func(t *testing.T) { - t.Parallel() httpmock.Activate() defer httpmock.DeactivateAndReset() From 50d7a8d27ea0caf3aca0b7c2ab0a2d13cd88cde5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9?= Date: Sat, 12 Jul 2025 22:26:31 +0200 Subject: [PATCH 18/27] update dependencies --- go.mod | 31 ++++++++++++++------------ go.sum | 70 ++++++++++++++++++++++++++++++++-------------------------- 2 files changed, 56 insertions(+), 45 deletions(-) diff --git a/go.mod b/go.mod index 787d3e4..ad22504 100644 --- a/go.mod +++ b/go.mod @@ -3,22 +3,24 @@ module github.com/gofiber/cli go 1.24 require ( - github.com/Masterminds/semver/v3 v3.3.1 - github.com/charmbracelet/bubbles v0.20.0 - github.com/charmbracelet/bubbletea v1.2.4 - github.com/containerd/console v1.0.4 - github.com/fsnotify/fsnotify v1.8.0 - github.com/jarcoal/httpmock v1.3.1 - github.com/muesli/termenv v0.15.2 - github.com/spf13/cobra v1.8.1 + github.com/Masterminds/semver/v3 v3.4.0 + github.com/charmbracelet/bubbles v0.21.0 + github.com/charmbracelet/bubbletea v1.3.6 + github.com/containerd/console v1.0.5 + github.com/fsnotify/fsnotify v1.9.0 + github.com/jarcoal/httpmock v1.4.0 + github.com/muesli/termenv v0.16.0 + github.com/spf13/cobra v1.9.1 github.com/stretchr/testify v1.10.0 ) require ( github.com/atotto/clipboard v0.1.4 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect - github.com/charmbracelet/lipgloss v1.0.0 // indirect - github.com/charmbracelet/x/ansi v0.5.2 // indirect + github.com/charmbracelet/colorprofile v0.3.1 // indirect + github.com/charmbracelet/lipgloss v1.1.0 // indirect + github.com/charmbracelet/x/ansi v0.9.3 // indirect + github.com/charmbracelet/x/cellbuf v0.0.13 // indirect github.com/charmbracelet/x/term v0.2.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect @@ -33,10 +35,11 @@ require ( github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect - github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/sync v0.9.0 // indirect - golang.org/x/sys v0.27.0 // indirect - golang.org/x/text v0.20.0 // indirect + github.com/spf13/pflag v1.0.6 // indirect + github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect + golang.org/x/sync v0.16.0 // indirect + golang.org/x/sys v0.34.0 // indirect + golang.org/x/text v0.27.0 // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 8fb4e8f..4991c07 100644 --- a/go.sum +++ b/go.sum @@ -1,33 +1,37 @@ -github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4= -github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= +github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= -github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE= -github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU= -github.com/charmbracelet/bubbletea v1.2.4 h1:KN8aCViA0eps9SCOThb2/XPIlea3ANJLUkv3KnQRNCE= -github.com/charmbracelet/bubbletea v1.2.4/go.mod h1:Qr6fVQw+wX7JkWWkVyXYk/ZUQ92a6XNekLXa3rR18MM= -github.com/charmbracelet/lipgloss v1.0.0 h1:O7VkGDvqEdGi93X+DeqsQ7PKHDgtQfF8j8/O2qFMQNg= -github.com/charmbracelet/lipgloss v1.0.0/go.mod h1:U5fy9Z+C38obMs+T+tJqst9VGzlOYGj4ri9reL3qUlo= -github.com/charmbracelet/x/ansi v0.5.2 h1:dEa1x2qdOZXD/6439s+wF7xjV+kZLu/iN00GuXXrU9E= -github.com/charmbracelet/x/ansi v0.5.2/go.mod h1:KBUFw1la39nl0dLl10l5ORDAqGXaeurTQmwyyVKse/Q= +github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs= +github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg= +github.com/charmbracelet/bubbletea v1.3.6 h1:VkHIxPJQeDt0aFJIsVxw8BQdh/F/L2KKZGsK6et5taU= +github.com/charmbracelet/bubbletea v1.3.6/go.mod h1:oQD9VCRQFF8KplacJLo28/jofOI2ToOfGYeFgBBxHOc= +github.com/charmbracelet/colorprofile v0.3.1 h1:k8dTHMd7fgw4bnFd7jXTLZrSU/CQrKnL3m+AxCzDz40= +github.com/charmbracelet/colorprofile v0.3.1/go.mod h1:/GkGusxNs8VB/RSOh3fu0TJmQ4ICMMPApIIVn0KszZ0= +github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY= +github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30= +github.com/charmbracelet/x/ansi v0.9.3 h1:BXt5DHS/MKF+LjuK4huWrC6NCvHtexww7dMayh6GXd0= +github.com/charmbracelet/x/ansi v0.9.3/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE= +github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k= +github.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= -github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro= -github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/qqsc= +github.com/containerd/console v1.0.5/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= -github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= -github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww= -github.com/jarcoal/httpmock v1.3.1/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg= +github.com/jarcoal/httpmock v1.4.0 h1:BvhqnH0JAYbNudL2GMJKgOHe2CtKlzJ/5rWKyp+hc2k= +github.com/jarcoal/httpmock v1.4.0/go.mod h1:ftW1xULwo+j0R0JJkJIIi7UKigZUXCLLanykgjwBXL0= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -40,14 +44,14 @@ github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2J github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g= -github.com/maxatome/go-testdeep v1.12.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM= +github.com/maxatome/go-testdeep v1.14.0 h1:rRlLv1+kI8eOI3OaBXZwb3O7xY3exRzdW5QyX48g9wI= +github.com/maxatome/go-testdeep v1.14.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM= github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= -github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= -github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= +github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= +github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -56,21 +60,25 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= -golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= +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/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= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= -golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= +golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= +golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From ad0ce673e7536b3fa22f00ce6ac31e1298e105bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9?= Date: Sat, 12 Jul 2025 22:29:50 +0200 Subject: [PATCH 19/27] fix linting --- cmd/version_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/version_test.go b/cmd/version_test.go index a2d502c..1746f1d 100644 --- a/cmd/version_test.go +++ b/cmd/version_test.go @@ -44,7 +44,7 @@ func Test_Version_Current(t *testing.T) { defer teardownCurrentVersionFile() _, err := currentVersion() - at.Error(err) + require.Error(t, err) }) t.Run("match version", func(t *testing.T) { @@ -90,7 +90,7 @@ require ( defer teardownCurrentVersionFile() _, err := currentVersion() - at.Error(err) + require.Error(t, err) }) } @@ -118,7 +118,7 @@ func Test_Version_Latest(t *testing.T) { httpmock.RegisterResponder(http.MethodGet, latestVersionURL, httpmock.NewErrorResponder(errors.New("network error"))) _, err := LatestFiberVersion() - at.Error(err) + require.Error(t, err) }) t.Run("version matched", func(t *testing.T) { @@ -139,7 +139,7 @@ func Test_Version_Latest(t *testing.T) { httpmock.RegisterResponder(http.MethodGet, latestVersionURL, httpmock.NewBytesResponder(200, []byte("no version"))) _, err := LatestFiberVersion() - at.Error(err) + require.Error(t, err) }) } From b136398784b34520ae04124a186dc5c074049c26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9?= Date: Sat, 12 Jul 2025 22:44:38 +0200 Subject: [PATCH 20/27] test fix --- cmd/dev_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/dev_test.go b/cmd/dev_test.go index c2dfe64..d1eeb9e 100644 --- a/cmd/dev_test.go +++ b/cmd/dev_test.go @@ -119,10 +119,11 @@ func Test_Dev_Escort_WatchingFiles(t *testing.T) { ) e := getEscort() - e.hitCh = make(chan struct{}) + e.hitCh = make(chan struct{}, 2) e.w, err = fsnotify.NewWatcher() require.NoError(t, err) + defer func() { require.NoError(t, e.w.Close()) }() e.extensions = []string{"go"} e.watcherEvents = make(chan fsnotify.Event) From a989589dbe09685542c48ac39d05dc3e794a631c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9?= Date: Sat, 12 Jul 2025 23:06:51 +0200 Subject: [PATCH 21/27] add docu --- README.md | 17 +++++++++++++++++ cmd/internal/cmd.go | 7 +++++++ cmd/internal/helpers.go | 3 +++ cmd/internal/prompt.go | 8 ++++++++ cmd/internal/task.go | 7 +++++++ cmd/version.go | 2 ++ 6 files changed, 44 insertions(+) diff --git a/README.md b/README.md index b98541e..b6e2b58 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,23 @@ fiber new PROJECT [module name] [flags] -t, --template string basic|complex (default "basic") ``` +## fiber migrate + +### Synopsis + +Migrate Fiber project version to a newer version + +```bash +fiber migrate --to 3.0.0 +``` + +### Options + +```text + -t, --to string Migrate to a specific version e.g:3.0.0 Format: X.Y.Z + -h, --help help for migrate +``` + ## fiber upgrade ### Synopsis diff --git a/cmd/internal/cmd.go b/cmd/internal/cmd.go index e82db40..7936589 100644 --- a/cmd/internal/cmd.go +++ b/cmd/internal/cmd.go @@ -13,6 +13,7 @@ import ( "github.com/muesli/termenv" ) +// SpinnerCmd wraps an exec.Cmd and displays a spinner while it runs. type SpinnerCmd struct { err error p *tea.Program @@ -28,6 +29,8 @@ type SpinnerCmd struct { done bool } +// NewSpinnerCmd returns a SpinnerCmd that runs the given command. The optional +// title is shown alongside the spinner. func NewSpinnerCmd(cmd *exec.Cmd, title ...string) *SpinnerCmd { spinnerModel := spinner.New() spinnerModel.Spinner = spinner.Dot @@ -50,6 +53,7 @@ func NewSpinnerCmd(cmd *exec.Cmd, title ...string) *SpinnerCmd { return c } +// Init implements the tea.Model interface. func (t *SpinnerCmd) Init() tea.Cmd { return tea.Batch(t.start(), t.spinnerModel.Tick) } @@ -136,6 +140,7 @@ const spinnerCmdTemplate = ` ` +// Run executes the command and manages the spinner lifecycle. func (t *SpinnerCmd) Run() (err error) { if t.size, err = checkConsole(); err != nil { return err @@ -148,6 +153,7 @@ func (t *SpinnerCmd) Run() (err error) { return t.err } +// UpdateOutput retrieves lines from c and stores them for display. func (t *SpinnerCmd) UpdateOutput(c <-chan []byte) { select { case b := <-c: @@ -158,6 +164,7 @@ func (t *SpinnerCmd) UpdateOutput(c <-chan []byte) { } } +// watchOutput reads lines from rc and forwards them to out until an error occurs. func (t *SpinnerCmd) watchOutput(out chan<- []byte, rc io.ReadCloser) { defer func() { if err := rc.Close(); err != nil { diff --git a/cmd/internal/helpers.go b/cmd/internal/helpers.go index 1a17874..793b10a 100644 --- a/cmd/internal/helpers.go +++ b/cmd/internal/helpers.go @@ -28,8 +28,11 @@ func checkConsole() (size console.WinSize, err error) { return size, nil } +// FileProcessor processes the file content and returns the modified content. type FileProcessor func(content string) string +// ChangeFileContent walks through cwd and applies the processorFn to every Go +// file found. Files in a vendor directory are skipped. func ChangeFileContent(cwd string, processorFn FileProcessor) error { err := filepath.Walk(cwd, func(path string, info os.FileInfo, err error) error { if err != nil { diff --git a/cmd/internal/prompt.go b/cmd/internal/prompt.go index f2ec59d..72bf145 100644 --- a/cmd/internal/prompt.go +++ b/cmd/internal/prompt.go @@ -11,6 +11,7 @@ import ( type errMsg error +// Prompt represents a small interactive input prompt used in the CLI. type Prompt struct { err error p *tea.Program @@ -19,6 +20,7 @@ type Prompt struct { textInput input.Model } +// NewPrompt initializes a new Prompt with an optional placeholder value. func NewPrompt(title string, placeholder ...string) *Prompt { p := &Prompt{ title: title, @@ -34,6 +36,7 @@ func NewPrompt(title string, placeholder ...string) *Prompt { return p } +// YesOrNo runs the prompt and returns true if the answer resembles "yes". func (p *Prompt) YesOrNo() (bool, error) { answer, err := p.Answer() if err != nil { @@ -43,6 +46,7 @@ func (p *Prompt) YesOrNo() (bool, error) { return parseBool(answer), nil } +// parseBool returns true if the provided string represents a truthy value. func parseBool(str string) bool { switch str { case "1", "t", "T", "true", "TRUE", "True", "y", "Y", "yes", "Yes": @@ -51,6 +55,7 @@ func parseBool(str string) bool { return false } +// Answer displays the prompt and returns the user's input. func (p *Prompt) Answer() (result string, err error) { if _, err = checkConsole(); err != nil { return "", fmt.Errorf("check console: %w", err) @@ -62,12 +67,14 @@ func (p *Prompt) Answer() (result string, err error) { return p.answer, nil } +// Init initializes the bubbletea program for the prompt. func (p *Prompt) Init() tea.Cmd { p.textInput.Focus() return input.Blink } +// Update handles prompt events and updates its state. func (p *Prompt) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmd tea.Cmd @@ -95,6 +102,7 @@ func (p *Prompt) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return p, cmd } +// View renders the prompt UI. func (p *Prompt) View() string { return fmt.Sprintf( "%s\n\n%s\n\n%s\n\n", diff --git a/cmd/internal/task.go b/cmd/internal/task.go index 2ac90b3..eafb2be 100644 --- a/cmd/internal/task.go +++ b/cmd/internal/task.go @@ -8,8 +8,10 @@ import ( "github.com/muesli/termenv" ) +// Task is a function executed while the spinner is displayed. type Task func() error +// SpinnerTask runs a Task while showing a spinner animation. type SpinnerTask struct { err error p *tea.Program @@ -18,6 +20,7 @@ type SpinnerTask struct { spinnerModel spinner.Model } +// NewSpinnerTask creates a SpinnerTask with the provided title and Task. func NewSpinnerTask(title string, task Task) *SpinnerTask { spinnerModel := spinner.New() spinnerModel.Spinner = spinner.Dot @@ -33,6 +36,7 @@ func NewSpinnerTask(title string, task Task) *SpinnerTask { return at } +// Init implements the tea.Model interface. func (t *SpinnerTask) Init() tea.Cmd { return tea.Batch( func() tea.Msg { @@ -40,6 +44,7 @@ func (t *SpinnerTask) Init() tea.Cmd { }, t.spinnerModel.Tick) } +// Update handles spinner events and updates its state. func (t *SpinnerTask) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: @@ -62,6 +67,7 @@ func (t *SpinnerTask) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } } +// View renders the spinner view. func (t *SpinnerTask) View() string { if t.err != nil { return "" @@ -75,6 +81,7 @@ func (t *SpinnerTask) View() string { return fmt.Sprintf("\n %s %s\n\n(esc/q/ctrl+c to quit)\n\n", s, t.title) } +// Run executes the task while showing a spinner. func (t *SpinnerTask) Run() (err error) { if _, err = checkConsole(); err != nil { return err diff --git a/cmd/version.go b/cmd/version.go index c854bdd..67133bd 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -58,10 +58,12 @@ func currentVersion() (string, error) { var latestVersionRegexp = regexp.MustCompile(`"name":\s*?"v(.*?)"`) +// LatestFiberVersion retrieves the most recent Fiber release version from GitHub. func LatestFiberVersion() (string, error) { return latestVersionByURL("https://api.github.com/repos/gofiber/fiber/releases/latest") } +// LatestCliVersion retrieves the latest Fiber CLI release version from GitHub. func LatestCliVersion() (string, error) { return latestVersionByURL("https://api.github.com/repos/gofiber/cli/releases/latest") } From 79d26d2b1f49e71e77741a8f87cbc3af41457989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9?= Date: Sat, 12 Jul 2025 23:15:46 +0200 Subject: [PATCH 22/27] add more migration methods --- cmd/internal/migrations/lists.go | 7 + cmd/internal/migrations/v3/common.go | 110 ++++++++++++++ cmd/internal/migrations/v3/common_test.go | 170 ++++++++++++++++++++++ 3 files changed, 287 insertions(+) diff --git a/cmd/internal/migrations/lists.go b/cmd/internal/migrations/lists.go index a543a09..b877091 100644 --- a/cmd/internal/migrations/lists.go +++ b/cmd/internal/migrations/lists.go @@ -31,6 +31,13 @@ var Migrations = []Migration{ v3migrations.MigrateParserMethods, v3migrations.MigrateRedirectMethods, v3migrations.MigrateGenericHelpers, + v3migrations.MigrateAddMethod, + v3migrations.MigrateMimeConstants, + v3migrations.MigrateLoggerTags, + v3migrations.MigrateAllParams, + v3migrations.MigrateMount, + v3migrations.MigrateRouterAddSignature, + v3migrations.MigrateContextMethods, }, }, {From: ">=1.0.0", To: ">=0.0.0-0", Functions: []MigrationFn{MigrateGoPkgs}}, diff --git a/cmd/internal/migrations/v3/common.go b/cmd/internal/migrations/v3/common.go index bc73d1a..12969c0 100644 --- a/cmd/internal/migrations/v3/common.go +++ b/cmd/internal/migrations/v3/common.go @@ -90,3 +90,113 @@ func MigrateGenericHelpers(cmd *cobra.Command, cwd string, _, _ *semver.Version) cmd.Println("Migrating generic helpers") return nil } + +// MigrateContextMethods updates context related methods to the new names +func MigrateContextMethods(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { + replacer := strings.NewReplacer( + ".Context()", ".RequestCtx()", + ".UserContext()", ".Context()", + ".SetUserContext(", ".SetContext(", + ) + + err := internal.ChangeFileContent(cwd, func(content string) string { + return replacer.Replace(content) + }) + if err != nil { + return fmt.Errorf("failed to migrate context methods: %w", err) + } + + cmd.Println("Migrating context methods") + 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(") + + err := internal.ChangeFileContent(cwd, func(content string) string { + return replacer.Replace(content) + }) + if err != nil { + return fmt.Errorf("failed to migrate Mount usages: %w", err) + } + + cmd.Println("Migrating Mount usages") + return nil +} + +// MigrateRouterAddSignature updates Router.Add signature to use a slice of methods +func MigrateRouterAddSignature(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { + err := internal.ChangeFileContent(cwd, func(content string) string { + re := regexp.MustCompile(`\.Add\((\s*[^,]+\s*),`) + return re.ReplaceAllString(content, ".Add([]string{$1},") + }) + if err != nil { + return fmt.Errorf("failed to migrate Router.Add signature: %w", err) + } + + cmd.Println("Migrating Router.Add signature") + return nil +} + +// MigrateAddMethod adapts the Add method signature +func MigrateAddMethod(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { + 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 { + return fmt.Errorf("failed to migrate Add method calls: %w", err) + } + + cmd.Println("Migrating Add method calls") + return nil +} + +// MigrateMimeConstants updates deprecated MIME constants +func MigrateMimeConstants(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { + replacer := strings.NewReplacer( + "MIMEApplicationJavaScriptCharsetUTF8", "MIMETextJavaScriptCharsetUTF8", + "MIMEApplicationJavaScript", "MIMETextJavaScript", + ) + + err := internal.ChangeFileContent(cwd, func(content string) string { + return replacer.Replace(content) + }) + if err != nil { + return fmt.Errorf("failed to migrate MIME constants: %w", err) + } + + cmd.Println("Migrating MIME constants") + return nil +} + +// MigrateLoggerTags updates deprecated logger tag constants +func MigrateLoggerTags(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { + err := internal.ChangeFileContent(cwd, func(content string) string { + return strings.ReplaceAll(content, "logger.TagHeader", "logger.TagReqHeader") + }) + if err != nil { + return fmt.Errorf("failed to migrate logger tags: %w", err) + } + + cmd.Println("Migrating logger tag constants") + return nil +} diff --git a/cmd/internal/migrations/v3/common_test.go b/cmd/internal/migrations/v3/common_test.go index eb3e049..131fe11 100644 --- a/cmd/internal/migrations/v3/common_test.go +++ b/cmd/internal/migrations/v3/common_test.go @@ -143,3 +143,173 @@ func handler(c fiber.Ctx) error { assert.Contains(t, content, "fiber.Query[bool](c, \"ok\"") assert.Contains(t, buf.String(), "Migrating generic helpers") } + +func Test_MigrateContextMethods(t *testing.T) { + t.Parallel() + + dir, err := os.MkdirTemp("", "mcmtest") + 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 { + ctx := c.Context() + uc := c.UserContext() + c.SetUserContext(ctx) + _ = uc + return nil +} +`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + require.NoError(t, MigrateContextMethods(cmd, dir, nil, nil)) + + content := readFile(t, file) + assert.Contains(t, content, ".RequestCtx()") + assert.Contains(t, content, ".Context()") + assert.Contains(t, content, ".SetContext(") + assert.Contains(t, buf.String(), "Migrating context methods") +} + +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() + + dir, err := os.MkdirTemp("", "mmtest") + 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 main() { + app := fiber.New() + api := fiber.New() + app.Mount("/api", api) +}`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + require.NoError(t, MigrateMount(cmd, dir, nil, nil)) + + content := readFile(t, file) + assert.Contains(t, content, ".Use(\"/api\", api)") + assert.Contains(t, buf.String(), "Migrating Mount usages") +} + +func Test_MigrateRouterAddSignature(t *testing.T) { + t.Parallel() + + dir, err := os.MkdirTemp("", "maddtest") + 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 main() { + app := fiber.New() + app.Add(fiber.MethodGet, "/", func(c fiber.Ctx) error { return nil }) +}`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + require.NoError(t, MigrateRouterAddSignature(cmd, dir, nil, nil)) + + content := readFile(t, file) + assert.Contains(t, content, "app.Add([]string{fiber.MethodGet}, \"/\"") + assert.Contains(t, buf.String(), "Migrating Router.Add signature") +} + +func Test_MigrateAddMethod(t *testing.T) { + t.Parallel() + + dir, err := os.MkdirTemp("", "maddtest") + 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 main() { + app := fiber.New() + app.Add(fiber.MethodGet, "/foo", func(c fiber.Ctx) error { return nil }) +} +`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + require.NoError(t, MigrateAddMethod(cmd, dir, nil, nil)) + + content := readFile(t, file) + assert.Contains(t, content, `Add([]string{fiber.MethodGet}, "/foo"`) + assert.Contains(t, buf.String(), "Migrating Add method calls") +} + +func Test_MigrateMimeConstants(t *testing.T) { + t.Parallel() + + dir, err := os.MkdirTemp("", "mmimetest") + require.NoError(t, err) + defer func() { require.NoError(t, os.RemoveAll(dir)) }() + + file := writeTempFile(t, dir, `package main +import "github.com/gofiber/fiber/v2" +const mime = fiber.MIMEApplicationJavaScript +`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + require.NoError(t, MigrateMimeConstants(cmd, dir, nil, nil)) + + content := readFile(t, file) + assert.NotContains(t, content, "MIMEApplicationJavaScript") + assert.Contains(t, content, "MIMETextJavaScript") + assert.Contains(t, buf.String(), "Migrating MIME constants") +} + +func Test_MigrateLoggerTags(t *testing.T) { + t.Parallel() + + dir, err := os.MkdirTemp("", "mloggertest") + require.NoError(t, err) + defer func() { require.NoError(t, os.RemoveAll(dir)) }() + + file := writeTempFile(t, dir, `package main +import ( + "github.com/gofiber/fiber/v2/middleware/logger" +) +var _ = logger.TagHeader +`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + require.NoError(t, MigrateLoggerTags(cmd, dir, nil, nil)) + + content := readFile(t, file) + assert.NotContains(t, content, "TagHeader") + assert.Contains(t, content, "TagReqHeader") + assert.Contains(t, buf.String(), "Migrating logger tag constants") +} From 4952b63c65ab4d0f2a12753c3c529915b6ec948c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9?= Date: Sat, 12 Jul 2025 23:32:17 +0200 Subject: [PATCH 23/27] add more migration methods --- cmd/internal/migrations/lists.go | 8 +- cmd/internal/migrations/v3/common.go | 159 +++++++++++++++++-- cmd/internal/migrations/v3/common_test.go | 177 +++++++++++++++++++--- 3 files changed, 309 insertions(+), 35 deletions(-) diff --git a/cmd/internal/migrations/lists.go b/cmd/internal/migrations/lists.go index b877091..2b91c8d 100644 --- a/cmd/internal/migrations/lists.go +++ b/cmd/internal/migrations/lists.go @@ -34,10 +34,16 @@ var Migrations = []Migration{ v3migrations.MigrateAddMethod, v3migrations.MigrateMimeConstants, v3migrations.MigrateLoggerTags, + v3migrations.MigrateStaticRoutes, + v3migrations.MigrateTrustedProxyConfig, v3migrations.MigrateAllParams, v3migrations.MigrateMount, - v3migrations.MigrateRouterAddSignature, + v3migrations.MigrateTrustedProxyConfig, v3migrations.MigrateContextMethods, + v3migrations.MigrateCORSConfig, + v3migrations.MigrateCSRFConfig, + v3migrations.MigrateMonitorImport, + v3migrations.MigrateProxyTLSConfig, }, }, {From: ">=1.0.0", To: ">=0.0.0-0", Functions: []MigrationFn{MigrateGoPkgs}}, diff --git a/cmd/internal/migrations/v3/common.go b/cmd/internal/migrations/v3/common.go index 12969c0..94134e0 100644 --- a/cmd/internal/migrations/v3/common.go +++ b/cmd/internal/migrations/v3/common.go @@ -3,6 +3,7 @@ package v3 import ( "fmt" "regexp" + "strconv" "strings" semver "github.com/Masterminds/semver/v3" @@ -142,31 +143,103 @@ func MigrateMount(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { return nil } -// MigrateRouterAddSignature updates Router.Add signature to use a slice of methods -func MigrateRouterAddSignature(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { +// MigrateAddMethod adapts the Add method signature +func MigrateAddMethod(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { err := internal.ChangeFileContent(cwd, func(content string) string { - re := regexp.MustCompile(`\.Add\((\s*[^,]+\s*),`) + re := regexp.MustCompile(`\.Add\(\s*([^,\n]+)\s*,`) return re.ReplaceAllString(content, ".Add([]string{$1},") }) if err != nil { - return fmt.Errorf("failed to migrate Router.Add signature: %w", err) + return fmt.Errorf("failed to migrate Add method calls: %w", err) } - cmd.Println("Migrating Router.Add signature") + cmd.Println("Migrating Add method calls") return nil } -// MigrateAddMethod adapts the Add method signature -func MigrateAddMethod(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { +// MigrateCORSConfig updates cors middleware configuration fields +func MigrateCORSConfig(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { err := internal.ChangeFileContent(cwd, func(content string) string { - re := regexp.MustCompile(`\.Add\(\s*([^,\n]+)\s*,`) - return re.ReplaceAllString(content, ".Add([]string{$1},") + conv := func(src string, re *regexp.Regexp, field string) string { + return re.ReplaceAllStringFunc(src, func(s string) string { + matches := re.FindStringSubmatch(s) + if len(matches) < 2 { + return s + } + parts := strings.Split(matches[1], ",") + for i, p := range parts { + parts[i] = fmt.Sprintf("%q", strings.TrimSpace(p)) + } + return fmt.Sprintf("%s: []string{%s}", field, strings.Join(parts, ", ")) + }) + } + + 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 }) if err != nil { - return fmt.Errorf("failed to migrate Add method calls: %w", err) + return fmt.Errorf("failed to migrate CORS configs: %w", err) } - cmd.Println("Migrating Add method calls") + cmd.Println("Migrating CORS middleware configs") + return nil +} + +// MigrateCSRFConfig updates csrf middleware configuration fields +func MigrateCSRFConfig(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { + replacer := strings.NewReplacer("Expiration:", "IdleTimeout:") + + 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 { + return fmt.Errorf("failed to migrate CSRF configs: %w", err) + } + + cmd.Println("Migrating CSRF middleware configs") + return nil +} + +// MigrateMonitorImport updates monitor middleware import path +func MigrateMonitorImport(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { + err := internal.ChangeFileContent(cwd, func(content string) string { + return strings.ReplaceAll(content, + "github.com/gofiber/fiber/v2/middleware/monitor", + "github.com/gofiber/contrib/monitor") + }) + if err != nil { + return fmt.Errorf("failed to migrate monitor import: %w", err) + } + + cmd.Println("Migrating monitor middleware import") + return nil +} + +// MigrateProxyTLSConfig updates proxy TLS helper to new client configuration +func MigrateProxyTLSConfig(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { + err := internal.ChangeFileContent(cwd, func(content string) string { + re := regexp.MustCompile(`proxy\.WithTlsConfig\(([^)]+)\)`) + return re.ReplaceAllString(content, + "proxy.WithClient(&fasthttp.Client{TLSConfig: $1})") + }) + if err != nil { + return fmt.Errorf("failed to migrate proxy TLS config: %w", err) + } + + cmd.Println("Migrating proxy TLS config") return nil } @@ -200,3 +273,67 @@ func MigrateLoggerTags(cmd *cobra.Command, cwd string, _, _ *semver.Version) err cmd.Println("Migrating logger tag constants") return nil } + +// MigrateStaticRoutes replaces app.Static calls with static middleware +func MigrateStaticRoutes(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { + err := internal.ChangeFileContent(cwd, func(content string) string { + re := regexp.MustCompile(`\.Static\(\s*("[^"]*")\s*,\s*("[^"]*")(?:,\s*([^)]*))?\)`) + return re.ReplaceAllStringFunc(content, func(m string) string { + sub := re.FindStringSubmatch(m) + pathLit := sub[1] + root := sub[2] + cfg := sub[3] + + path, err := strconv.Unquote(pathLit) + if err != nil { + path = strings.Trim(pathLit, "\"") + } + + switch path { + case "/": + path = "/*" + case "*": + // keep as is + default: + path += "*" + } + + quoted := strconv.Quote(path) + + if cfg != "" { + cfg = strings.TrimSpace(cfg) + cfg = strings.Replace(cfg, "Static{", "static.Config{", 1) + reIndex := regexp.MustCompile(`Index:\s*([^,}\n]+)`) + cfg = reIndex.ReplaceAllString(cfg, "IndexNames: []string{$1}") + return fmt.Sprintf(".Get(%s, static.New(%s, %s))", quoted, root, cfg) + } + + return fmt.Sprintf(".Get(%s, static.New(%s))", quoted, root) + }) + }) + if err != nil { + return fmt.Errorf("failed to migrate static usages: %w", err) + } + + cmd.Println("Migrating app.Static usage") + return nil +} + +// MigrateTrustedProxyConfig updates trusted proxy configuration options +func MigrateTrustedProxyConfig(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { + 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 + }) + if err != nil { + return fmt.Errorf("failed to migrate trusted proxy config: %w", err) + } + + cmd.Println("Migrating trusted proxy config") + return nil +} diff --git a/cmd/internal/migrations/v3/common_test.go b/cmd/internal/migrations/v3/common_test.go index 131fe11..43ed1d9 100644 --- a/cmd/internal/migrations/v3/common_test.go +++ b/cmd/internal/migrations/v3/common_test.go @@ -221,29 +221,6 @@ func main() { assert.Contains(t, buf.String(), "Migrating Mount usages") } -func Test_MigrateRouterAddSignature(t *testing.T) { - t.Parallel() - - dir, err := os.MkdirTemp("", "maddtest") - 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 main() { - app := fiber.New() - app.Add(fiber.MethodGet, "/", func(c fiber.Ctx) error { return nil }) -}`) - - var buf bytes.Buffer - cmd := newCmd(&buf) - require.NoError(t, MigrateRouterAddSignature(cmd, dir, nil, nil)) - - content := readFile(t, file) - assert.Contains(t, content, "app.Add([]string{fiber.MethodGet}, \"/\"") - assert.Contains(t, buf.String(), "Migrating Router.Add signature") -} - func Test_MigrateAddMethod(t *testing.T) { t.Parallel() @@ -313,3 +290,157 @@ var _ = logger.TagHeader assert.Contains(t, content, "TagReqHeader") assert.Contains(t, buf.String(), "Migrating logger tag constants") } + +func Test_MigrateStaticRoutes(t *testing.T) { + t.Parallel() + + dir, err := os.MkdirTemp("", "msrtest") + 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 main() { + app := fiber.New() + app.Static("/", "./public") + app.Static("/prefix", "./public", Static{Index: "index.htm"}) +}`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + require.NoError(t, MigrateStaticRoutes(cmd, dir, nil, nil)) + + content := readFile(t, file) + assert.Contains(t, content, `.Get("/*", static.New("./public"))`) + assert.Contains(t, content, `static.New("./public", static.Config{IndexNames: []string{"index.htm"}})`) + assert.Contains(t, buf.String(), "Migrating app.Static usage") +} + +func Test_MigrateTrustedProxyConfig(t *testing.T) { + t.Parallel() + + dir, err := os.MkdirTemp("", "mtpctest") + 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 main() { + app := fiber.New(fiber.Config{ + EnableTrustedProxyCheck: true, + TrustedProxies: []string{"0.8.0.0"}, + }) + _ = app +}`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + require.NoError(t, MigrateTrustedProxyConfig(cmd, dir, nil, nil)) + + content := readFile(t, file) + assert.Contains(t, content, "TrustProxy: true") + assert.Contains(t, content, "TrustProxyConfig: fiber.TrustProxyConfig{Proxies: []string{\"0.8.0.0\"}},") + assert.Contains(t, buf.String(), "Migrating trusted proxy config") +} + +func Test_MigrateCORSConfig(t *testing.T) { + t.Parallel() + + dir, err := os.MkdirTemp("", "mcors") + require.NoError(t, err) + defer func() { require.NoError(t, os.RemoveAll(dir)) }() + + file := writeTempFile(t, dir, `package main +import "github.com/gofiber/fiber/v2/middleware/cors" +var _ = cors.New(cors.Config{ + AllowOrigins: "https://a.com,https://b.com", + AllowMethods: "GET,POST", + AllowHeaders: "Content-Type", + ExposeHeaders: "Content-Length", +})`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + require.NoError(t, MigrateCORSConfig(cmd, dir, nil, nil)) + + content := readFile(t, file) + assert.Contains(t, content, `AllowOrigins: []string{"https://a.com", "https://b.com"}`) + assert.Contains(t, content, `AllowMethods: []string{"GET", "POST"}`) + assert.Contains(t, content, `AllowHeaders: []string{"Content-Type"}`) + assert.Contains(t, content, `ExposeHeaders: []string{"Content-Length"}`) + assert.Contains(t, buf.String(), "Migrating CORS middleware configs") +} + +func Test_MigrateCSRFConfig(t *testing.T) { + t.Parallel() + + dir, err := os.MkdirTemp("", "mcsrf") + require.NoError(t, err) + defer func() { require.NoError(t, os.RemoveAll(dir)) }() + + file := writeTempFile(t, dir, `package main +import ( + "github.com/gofiber/fiber/v2/middleware/csrf" + "time" +) +var _ = csrf.New(csrf.Config{ + Expiration: 10 * time.Minute, + SessionKey: "csrf", +})`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + require.NoError(t, MigrateCSRFConfig(cmd, dir, nil, nil)) + + content := readFile(t, file) + assert.Contains(t, content, "IdleTimeout:") + assert.NotContains(t, content, "Expiration:") + assert.NotContains(t, content, "SessionKey") + assert.Contains(t, buf.String(), "Migrating CSRF middleware configs") +} + +func Test_MigrateMonitorImport(t *testing.T) { + t.Parallel() + + dir, err := os.MkdirTemp("", "mmonitor") + require.NoError(t, err) + defer func() { require.NoError(t, os.RemoveAll(dir)) }() + + file := writeTempFile(t, dir, `package main +import "github.com/gofiber/fiber/v2/middleware/monitor" +var _ = monitor.New()`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + require.NoError(t, MigrateMonitorImport(cmd, dir, nil, nil)) + + content := readFile(t, file) + assert.Contains(t, content, "github.com/gofiber/contrib/monitor") + assert.NotContains(t, content, "fiber/v2/middleware/monitor") + assert.Contains(t, buf.String(), "Migrating monitor middleware import") +} + +func Test_MigrateProxyTLSConfig(t *testing.T) { + t.Parallel() + + dir, err := os.MkdirTemp("", "mproxy") + require.NoError(t, err) + defer func() { require.NoError(t, os.RemoveAll(dir)) }() + + file := writeTempFile(t, dir, `package main +import ( + "github.com/gofiber/fiber/v2/middleware/proxy" + "crypto/tls" +) +func main() { + proxy.WithTlsConfig(&tls.Config{InsecureSkipVerify: true}) +}`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + require.NoError(t, MigrateProxyTLSConfig(cmd, dir, nil, nil)) + + content := readFile(t, file) + assert.Contains(t, content, "proxy.WithClient(&fasthttp.Client{TLSConfig: &tls.Config{InsecureSkipVerify: true}})") + assert.Contains(t, buf.String(), "Migrating proxy TLS config") +} From f5066b07a196280e19bee49580edf3fe239767f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9?= Date: Sat, 12 Jul 2025 23:36:44 +0200 Subject: [PATCH 24/27] add more migration methods --- cmd/internal/migrations/lists.go | 4 ++-- cmd/internal/migrations/v3/common.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/internal/migrations/lists.go b/cmd/internal/migrations/lists.go index 2b91c8d..ac5e912 100644 --- a/cmd/internal/migrations/lists.go +++ b/cmd/internal/migrations/lists.go @@ -23,12 +23,14 @@ type Migration struct { // Example structure: // {"from": ">=2.0.0", "to": "<=3.*.*", "fn": [MigrateFN, MigrateFN]} var Migrations = []Migration{ + {From: ">=1.0.0", To: ">=0.0.0-0", Functions: []MigrationFn{MigrateGoPkgs}}, { From: ">=2.0.0", To: "<4.0.0-0", Functions: []MigrationFn{ v3migrations.MigrateHandlerSignatures, v3migrations.MigrateParserMethods, + v3migrations.MigrateAllParams, v3migrations.MigrateRedirectMethods, v3migrations.MigrateGenericHelpers, v3migrations.MigrateAddMethod, @@ -36,7 +38,6 @@ var Migrations = []Migration{ v3migrations.MigrateLoggerTags, v3migrations.MigrateStaticRoutes, v3migrations.MigrateTrustedProxyConfig, - v3migrations.MigrateAllParams, v3migrations.MigrateMount, v3migrations.MigrateTrustedProxyConfig, v3migrations.MigrateContextMethods, @@ -46,7 +47,6 @@ var Migrations = []Migration{ v3migrations.MigrateProxyTLSConfig, }, }, - {From: ">=1.0.0", To: ">=0.0.0-0", Functions: []MigrationFn{MigrateGoPkgs}}, } // DoMigration runs all migrations diff --git a/cmd/internal/migrations/v3/common.go b/cmd/internal/migrations/v3/common.go index 94134e0..0f41d92 100644 --- a/cmd/internal/migrations/v3/common.go +++ b/cmd/internal/migrations/v3/common.go @@ -97,7 +97,7 @@ func MigrateContextMethods(cmd *cobra.Command, cwd string, _, _ *semver.Version) replacer := strings.NewReplacer( ".Context()", ".RequestCtx()", ".UserContext()", ".Context()", - ".SetUserContext(", ".SetContext(", + ".SetUserContext(", ".SetContext(", // TODO: check if this is correct ) err := internal.ChangeFileContent(cwd, func(content string) string { From 20a4ac46f4fc67e0927307d08a48ebaaa34c0717 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9?= Date: Sat, 12 Jul 2025 23:43:37 +0200 Subject: [PATCH 25/27] add more migration methods --- cmd/internal/migrations/lists.go | 6 + cmd/internal/migrations/v3/common.go | 118 ++++++++++++++++ cmd/internal/migrations/v3/common_test.go | 158 ++++++++++++++++++++++ 3 files changed, 282 insertions(+) diff --git a/cmd/internal/migrations/lists.go b/cmd/internal/migrations/lists.go index ac5e912..0db2ffd 100644 --- a/cmd/internal/migrations/lists.go +++ b/cmd/internal/migrations/lists.go @@ -41,10 +41,16 @@ var Migrations = []Migration{ v3migrations.MigrateMount, v3migrations.MigrateTrustedProxyConfig, v3migrations.MigrateContextMethods, + v3migrations.MigrateViewBind, v3migrations.MigrateCORSConfig, v3migrations.MigrateCSRFConfig, v3migrations.MigrateMonitorImport, v3migrations.MigrateProxyTLSConfig, + v3migrations.MigrateAppTestConfig, + v3migrations.MigrateMiddlewareLocals, + v3migrations.MigrateFilesystemMiddleware, + v3migrations.MigrateLimiterConfig, + v3migrations.MigrateEnvVarConfig, }, }, } diff --git a/cmd/internal/migrations/v3/common.go b/cmd/internal/migrations/v3/common.go index 0f41d92..e247db5 100644 --- a/cmd/internal/migrations/v3/common.go +++ b/cmd/internal/migrations/v3/common.go @@ -111,6 +111,21 @@ func MigrateContextMethods(cmd *cobra.Command, cwd string, _, _ *semver.Version) return nil } +// 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(") + + err := internal.ChangeFileContent(cwd, func(content string) string { + return replacer.Replace(content) + }) + if err != nil { + return fmt.Errorf("failed to migrate ViewBind calls: %w", err) + } + + cmd.Println("Migrating view binding helpers") + 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( @@ -337,3 +352,106 @@ func MigrateTrustedProxyConfig(cmd *cobra.Command, cwd string, _, _ *semver.Vers cmd.Println("Migrating trusted proxy config") return nil } + +// MigrateFilesystemMiddleware replaces filesystem middleware with static middleware +func MigrateFilesystemMiddleware(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { + err := internal.ChangeFileContent(cwd, func(content string) string { + content = strings.ReplaceAll(content, + "github.com/gofiber/fiber/v2/middleware/filesystem", + "github.com/gofiber/fiber/v3/middleware/static") + content = strings.ReplaceAll(content, + "github.com/gofiber/fiber/v3/middleware/filesystem", + "github.com/gofiber/fiber/v3/middleware/static") + + content = strings.ReplaceAll(content, "filesystem.New(", "static.New(\"\", ") + content = strings.ReplaceAll(content, "filesystem.Config{", "static.Config{") + content = strings.ReplaceAll(content, "Root:", "FS:") + content = strings.ReplaceAll(content, "http.Dir(", "os.DirFS(") + + reIndex := regexp.MustCompile(`Index:\s*([^,}\n]+)`) + content = reIndex.ReplaceAllString(content, "IndexNames: []string{$1}") + + return content + }) + if err != nil { + return fmt.Errorf("failed to migrate filesystem middleware: %w", err) + } + + cmd.Println("Migrating filesystem middleware usage") + return nil +} + +// MigrateLimiterConfig updates limiter middleware configuration fields +func MigrateLimiterConfig(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { + err := internal.ChangeFileContent(cwd, func(content string) string { + reConfig := regexp.MustCompile(`limiter\.Config{[^}]*}`) + return reConfig.ReplaceAllStringFunc(content, func(s string) string { + s = strings.ReplaceAll(s, "Duration:", "Expiration:") + s = strings.ReplaceAll(s, "Store:", "Storage:") + s = strings.ReplaceAll(s, "Key:", "KeyGenerator:") + return s + }) + }) + if err != nil { + return fmt.Errorf("failed to migrate limiter configs: %w", err) + } + + cmd.Println("Migrating limiter middleware configs") + return nil +} + +// MigrateEnvVarConfig removes deprecated fields from envvar middleware configuration +func MigrateEnvVarConfig(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { + err := internal.ChangeFileContent(cwd, func(content string) string { + re := regexp.MustCompile(`\s*ExcludeVars:\s*[^,]+,?\n`) + return re.ReplaceAllString(content, "") + }) + if err != nil { + return fmt.Errorf("failed to migrate envvar configs: %w", err) + } + + cmd.Println("Migrating envvar middleware configs") + return nil +} + +// MigrateAppTestConfig updates app.Test calls to use the new TestConfig parameter +func MigrateAppTestConfig(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { + err := internal.ChangeFileContent(cwd, func(content string) string { + re := regexp.MustCompile(`\.Test\(([^,\n]+),\s*([^\n)]+)\)`) + return re.ReplaceAllString(content, `.Test($1, fiber.TestConfig{Timeout: $2})`) + }) + if err != nil { + return fmt.Errorf("failed to migrate app.Test calls: %w", err) + } + + cmd.Println("Migrating app.Test usages") + return nil +} + +// MigrateMiddlewareLocals replaces Locals lookups for middleware data with helper functions +func MigrateMiddlewareLocals(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { + err := internal.ChangeFileContent(cwd, func(content string) string { + replacements := []struct { + re *regexp.Regexp + repl string + }{ + {regexp.MustCompile(`(\w+)\.Locals\("requestid"\)`), `requestid.FromContext($1)`}, + {regexp.MustCompile(`(\w+)\.Locals\("csrf"\)`), `csrf.TokenFromContext($1)`}, + {regexp.MustCompile(`(\w+)\.Locals\("csrf_handler"\)`), `csrf.HandlerFromContext($1)`}, + {regexp.MustCompile(`(\w+)\.Locals\("session"\)`), `session.FromContext($1)`}, + {regexp.MustCompile(`(\w+)\.Locals\("username"\)`), `basicauth.UsernameFromContext($1)`}, + {regexp.MustCompile(`(\w+)\.Locals\("password"\)`), `basicauth.PasswordFromContext($1)`}, + {regexp.MustCompile(`(\w+)\.Locals\("token"\)`), `keyauth.TokenFromContext($1)`}, + } + for _, r := range replacements { + content = r.re.ReplaceAllString(content, r.repl) + } + return content + }) + if err != nil { + return fmt.Errorf("failed to migrate middleware locals: %w", err) + } + + cmd.Println("Migrating middleware locals") + return nil +} diff --git a/cmd/internal/migrations/v3/common_test.go b/cmd/internal/migrations/v3/common_test.go index 43ed1d9..1c00242 100644 --- a/cmd/internal/migrations/v3/common_test.go +++ b/cmd/internal/migrations/v3/common_test.go @@ -173,6 +173,29 @@ func handler(c fiber.Ctx) error { assert.Contains(t, buf.String(), "Migrating context methods") } +func Test_MigrateViewBind(t *testing.T) { + t.Parallel() + + dir, err := os.MkdirTemp("", "mvbtest") + 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 { + return c.Bind("index", fiber.Map{}) +}`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + require.NoError(t, MigrateViewBind(cmd, dir, nil, nil)) + + content := readFile(t, file) + assert.Contains(t, content, ".ViewBind(") + assert.NotContains(t, content, "c.Bind(") + assert.Contains(t, buf.String(), "Migrating view binding helpers") +} + func Test_MigrateAllParams(t *testing.T) { t.Parallel() @@ -444,3 +467,138 @@ func main() { assert.Contains(t, content, "proxy.WithClient(&fasthttp.Client{TLSConfig: &tls.Config{InsecureSkipVerify: true}})") assert.Contains(t, buf.String(), "Migrating proxy TLS config") } +func Test_MigrateFilesystemMiddleware(t *testing.T) { + t.Parallel() + + dir, err := os.MkdirTemp("", "mfstest") + require.NoError(t, err) + defer func() { require.NoError(t, os.RemoveAll(dir)) }() + + file := writeTempFile(t, dir, `package main +import ( + "net/http" + "github.com/gofiber/fiber/v2/middleware/filesystem" +) +var _ = filesystem.New(filesystem.Config{ + Root: http.Dir("./assets"), + Index: "index.html", +})`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + require.NoError(t, MigrateFilesystemMiddleware(cmd, dir, nil, nil)) + + content := readFile(t, file) + assert.Contains(t, content, "github.com/gofiber/fiber/v3/middleware/static") + assert.Contains(t, content, `static.New("", static.Config{`) + assert.Contains(t, content, `FS: os.DirFS("./assets")`) + assert.Contains(t, content, `IndexNames: []string{"index.html"}`) + assert.Contains(t, buf.String(), "Migrating filesystem middleware usage") +} + +func Test_MigrateLimiterConfig(t *testing.T) { + t.Parallel() + + dir, err := os.MkdirTemp("", "mlimit") + require.NoError(t, err) + defer func() { require.NoError(t, os.RemoveAll(dir)) }() + + file := writeTempFile(t, dir, `package main +import ( + "time" + "github.com/gofiber/fiber/v2/middleware/limiter" +) +var _ = limiter.New(limiter.Config{ + Duration: time.Second, + Store: nil, + Key: func(c fiber.Ctx) string { return "" }, +})`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + require.NoError(t, MigrateLimiterConfig(cmd, dir, nil, nil)) + + content := readFile(t, file) + assert.NotContains(t, content, "Duration:") + assert.Contains(t, content, "Expiration:") + assert.NotContains(t, content, "Store:") + assert.Contains(t, content, "Storage:") + assert.NotContains(t, content, "Key:") + assert.Contains(t, content, "KeyGenerator:") + assert.Contains(t, buf.String(), "Migrating limiter middleware configs") +} + +func Test_MigrateEnvVarConfig(t *testing.T) { + t.Parallel() + + dir, err := os.MkdirTemp("", "menvvar") + require.NoError(t, err) + defer func() { require.NoError(t, os.RemoveAll(dir)) }() + + file := writeTempFile(t, dir, `package main +import "github.com/gofiber/fiber/v2/middleware/envvar" +var _ = envvar.New(envvar.Config{ + ExportVars: []string{"A"}, + ExcludeVars: []string{"B"}, +})`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + require.NoError(t, MigrateEnvVarConfig(cmd, dir, nil, nil)) + + content := readFile(t, file) + assert.NotContains(t, content, "ExcludeVars") + assert.Contains(t, buf.String(), "Migrating envvar middleware configs") +} + +func Test_MigrateAppTestConfig(t *testing.T) { + t.Parallel() + + dir, err := os.MkdirTemp("", "mtestcfg") + require.NoError(t, err) + defer func() { require.NoError(t, os.RemoveAll(dir)) }() + + file := writeTempFile(t, dir, `package main +import ( + "github.com/gofiber/fiber/v2" + "net/http/httptest" + "time" +) +func main() { + app := fiber.New() + req := httptest.NewRequest(fiber.MethodGet, "/", nil) + _ = app.Test(req, 2*time.Second) +}`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + require.NoError(t, MigrateAppTestConfig(cmd, dir, nil, nil)) + + content := readFile(t, file) + assert.Contains(t, content, `app.Test(req, fiber.TestConfig{Timeout: 2*time.Second})`) + assert.Contains(t, buf.String(), "Migrating app.Test usages") +} + +func Test_MigrateMiddlewareLocals(t *testing.T) { + t.Parallel() + + dir, err := os.MkdirTemp("", "mlocals") + 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 { + id := c.Locals("requestid") + _ = id + return nil +}`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + require.NoError(t, MigrateMiddlewareLocals(cmd, dir, nil, nil)) + + content := readFile(t, file) + assert.Contains(t, content, `requestid.FromContext(c)`) + assert.Contains(t, buf.String(), "Migrating middleware locals") +} From cc1b11c7cfef08b818a455397a7503a00ef24b6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9?= Date: Sat, 12 Jul 2025 23:51:57 +0200 Subject: [PATCH 26/27] add more migration methods --- cmd/internal/migrations/lists.go | 2 + cmd/internal/migrations/v3/common.go | 111 ++++++++++++++---- cmd/internal/migrations/v3/common_test.go | 136 ++++++++++++++++++---- 3 files changed, 204 insertions(+), 45 deletions(-) diff --git a/cmd/internal/migrations/lists.go b/cmd/internal/migrations/lists.go index 0db2ffd..0e35878 100644 --- a/cmd/internal/migrations/lists.go +++ b/cmd/internal/migrations/lists.go @@ -40,6 +40,8 @@ var Migrations = []Migration{ v3migrations.MigrateTrustedProxyConfig, v3migrations.MigrateMount, v3migrations.MigrateTrustedProxyConfig, + v3migrations.MigrateConfigListenerFields, + v3migrations.MigrateListenerCallbacks, v3migrations.MigrateContextMethods, v3migrations.MigrateViewBind, v3migrations.MigrateCORSConfig, diff --git a/cmd/internal/migrations/v3/common.go b/cmd/internal/migrations/v3/common.go index e247db5..898eab7 100644 --- a/cmd/internal/migrations/v3/common.go +++ b/cmd/internal/migrations/v3/common.go @@ -353,7 +353,46 @@ func MigrateTrustedProxyConfig(cmd *cobra.Command, cwd string, _, _ *semver.Vers return nil } -// MigrateFilesystemMiddleware replaces filesystem middleware with static middleware +// MigrateConfigListenerFields updates config fields that have been moved or renamed +// in Fiber v3. It renames Prefork and Network fields and adapts them to the new +// listener configuration fields. +func MigrateConfigListenerFields(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { + err := internal.ChangeFileContent(cwd, func(content string) string { + replacer := strings.NewReplacer( + "Prefork:", "EnablePrefork:", + "Network:", "ListenerNetwork:", + ) + return replacer.Replace(content) + }) + if err != nil { + return fmt.Errorf("failed to migrate listener related config fields: %w", err) + } + + cmd.Println("Migrating listener related config fields") + return nil +} + +// 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 { + 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 + }) + if err != nil { + return fmt.Errorf("failed to migrate listener callbacks: %w", err) + } + + cmd.Println("Migrating listener callbacks") + return nil +} + +// MigrateFilesystemMiddleware replaces deprecated filesystem middleware with static middleware func MigrateFilesystemMiddleware(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { err := internal.ChangeFileContent(cwd, func(content string) string { content = strings.ReplaceAll(content, @@ -363,13 +402,19 @@ func MigrateFilesystemMiddleware(cmd *cobra.Command, cwd string, _, _ *semver.Ve "github.com/gofiber/fiber/v3/middleware/filesystem", "github.com/gofiber/fiber/v3/middleware/static") - content = strings.ReplaceAll(content, "filesystem.New(", "static.New(\"\", ") + reNew := regexp.MustCompile(`filesystem\.New\s*\(`) + content = reNew.ReplaceAllString(content, `static.New("", `) + content = strings.ReplaceAll(content, "filesystem.Config{", "static.Config{") - content = strings.ReplaceAll(content, "Root:", "FS:") - content = strings.ReplaceAll(content, "http.Dir(", "os.DirFS(") - reIndex := regexp.MustCompile(`Index:\s*([^,}\n]+)`) - content = reIndex.ReplaceAllString(content, "IndexNames: []string{$1}") + reRootHTTP := regexp.MustCompile(`Root:\s*http.Dir\(([^)]+)\)`) + content = reRootHTTP.ReplaceAllString(content, `FS: os.DirFS($1)`) + + reRoot := regexp.MustCompile(`Root:\s*([^,\n]+)`) + content = reRoot.ReplaceAllString(content, `FS: os.DirFS($1)`) + + reIndex := regexp.MustCompile(`Index:\s*([^,\n]+)`) + content = reIndex.ReplaceAllString(content, `IndexNames: []string{$1}`) return content }) @@ -377,7 +422,45 @@ func MigrateFilesystemMiddleware(cmd *cobra.Command, cwd string, _, _ *semver.Ve return fmt.Errorf("failed to migrate filesystem middleware: %w", err) } - cmd.Println("Migrating filesystem middleware usage") + cmd.Println("Migrating filesystem middleware") + return nil +} + +// MigrateEnvVarConfig removes deprecated ExcludeVars field from envvar middleware configuration +func MigrateEnvVarConfig(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { + err := internal.ChangeFileContent(cwd, func(content string) string { + re := regexp.MustCompile(`\s*ExcludeVars:\s*[^,]+,?\n`) + return re.ReplaceAllString(content, "") + }) + if err != nil { + return fmt.Errorf("failed to migrate EnvVar configs: %w", err) + } + + cmd.Println("Migrating EnvVar middleware configs") + return nil +} + +// MigrateHealthcheckConfig updates healthcheck middleware configuration fields +func MigrateHealthcheckConfig(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { + err := internal.ChangeFileContent(cwd, func(content string) string { + content = strings.ReplaceAll(content, "LivenessProbe:", "Probe:") + + re := regexp.MustCompile(`\s*ReadinessProbe:\s*[^,]+,?\n`) + content = re.ReplaceAllString(content, "") + + re = regexp.MustCompile(`\s*LivenessEndpoint:\s*[^,]+,?\n?`) + content = re.ReplaceAllString(content, "") + + re = regexp.MustCompile(`\s*ReadinessEndpoint:\s*[^,]+,?\n?`) + content = re.ReplaceAllString(content, "") + + return content + }) + if err != nil { + return fmt.Errorf("failed to migrate healthcheck configs: %w", err) + } + + cmd.Println("Migrating healthcheck middleware configs") return nil } @@ -400,20 +483,6 @@ func MigrateLimiterConfig(cmd *cobra.Command, cwd string, _, _ *semver.Version) return nil } -// MigrateEnvVarConfig removes deprecated fields from envvar middleware configuration -func MigrateEnvVarConfig(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { - err := internal.ChangeFileContent(cwd, func(content string) string { - re := regexp.MustCompile(`\s*ExcludeVars:\s*[^,]+,?\n`) - return re.ReplaceAllString(content, "") - }) - if err != nil { - return fmt.Errorf("failed to migrate envvar configs: %w", err) - } - - cmd.Println("Migrating envvar middleware configs") - return nil -} - // MigrateAppTestConfig updates app.Test calls to use the new TestConfig parameter func MigrateAppTestConfig(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { err := internal.ChangeFileContent(cwd, func(content string) string { diff --git a/cmd/internal/migrations/v3/common_test.go b/cmd/internal/migrations/v3/common_test.go index 1c00242..3cee65e 100644 --- a/cmd/internal/migrations/v3/common_test.go +++ b/cmd/internal/migrations/v3/common_test.go @@ -467,51 +467,136 @@ func main() { assert.Contains(t, content, "proxy.WithClient(&fasthttp.Client{TLSConfig: &tls.Config{InsecureSkipVerify: true}})") assert.Contains(t, buf.String(), "Migrating proxy TLS config") } + +func Test_MigrateConfigListenerFields(t *testing.T) { + t.Parallel() + + dir, err := os.MkdirTemp("", "mconf") + 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 main() { + app := fiber.New(fiber.Config{ + Prefork: true, + Network: "tcp", + }) + _ = app +}`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + require.NoError(t, MigrateConfigListenerFields(cmd, dir, nil, nil)) + + content := readFile(t, file) + assert.Contains(t, content, "EnablePrefork: true") + assert.Contains(t, content, "ListenerNetwork: \"tcp\"") + assert.Contains(t, buf.String(), "Migrating listener related config fields") +} + +func Test_MigrateListenerCallbacks(t *testing.T) { + t.Parallel() + + dir, err := os.MkdirTemp("", "mlistener") + require.NoError(t, err) + defer func() { require.NoError(t, os.RemoveAll(dir)) }() + + file := writeTempFile(t, dir, `package main +import ( + "github.com/gofiber/fiber/v2" + "log" +) +func main() { + app := fiber.New() + app.Listen(":3000", fiber.ListenerConfig{ + OnShutdownError: func(err error) { + log.Print(err) + }, + OnShutdownSuccess: func() { + log.Print("ok") + }, + }) +}`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + require.NoError(t, MigrateListenerCallbacks(cmd, dir, nil, nil)) + + content := readFile(t, file) + assert.NotContains(t, content, "OnShutdownError") + assert.NotContains(t, content, "OnShutdownSuccess") + assert.Contains(t, buf.String(), "Migrating listener callbacks") +} + func Test_MigrateFilesystemMiddleware(t *testing.T) { t.Parallel() - dir, err := os.MkdirTemp("", "mfstest") + dir, err := os.MkdirTemp("", "mfs") require.NoError(t, err) defer func() { require.NoError(t, os.RemoveAll(dir)) }() file := writeTempFile(t, dir, `package main import ( - "net/http" "github.com/gofiber/fiber/v2/middleware/filesystem" + "net/http" ) -var _ = filesystem.New(filesystem.Config{ - Root: http.Dir("./assets"), - Index: "index.html", -})`) +func main() { + _ = filesystem.New(filesystem.Config{ + Root: http.Dir("./assets"), + Index: "index.html", + }) +}`) var buf bytes.Buffer cmd := newCmd(&buf) require.NoError(t, MigrateFilesystemMiddleware(cmd, dir, nil, nil)) content := readFile(t, file) - assert.Contains(t, content, "github.com/gofiber/fiber/v3/middleware/static") assert.Contains(t, content, `static.New("", static.Config{`) assert.Contains(t, content, `FS: os.DirFS("./assets")`) assert.Contains(t, content, `IndexNames: []string{"index.html"}`) - assert.Contains(t, buf.String(), "Migrating filesystem middleware usage") + assert.Contains(t, buf.String(), "Migrating filesystem middleware") +} + +func Test_MigrateEnvVarConfig(t *testing.T) { + t.Parallel() + + dir, err := os.MkdirTemp("", "menvvar") + require.NoError(t, err) + defer func() { require.NoError(t, os.RemoveAll(dir)) }() + + file := writeTempFile(t, dir, `package main +import "github.com/gofiber/fiber/v2/middleware/envvar" +var _ = envvar.New(envvar.Config{ + ExcludeVars: []string{"SECRET"}, +})`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + require.NoError(t, MigrateEnvVarConfig(cmd, dir, nil, nil)) + + content := readFile(t, file) + assert.NotContains(t, content, "ExcludeVars") + assert.Contains(t, buf.String(), "Migrating EnvVar middleware configs") } func Test_MigrateLimiterConfig(t *testing.T) { t.Parallel() - dir, err := os.MkdirTemp("", "mlimit") + dir, err := os.MkdirTemp("", "mlimiter") require.NoError(t, err) defer func() { require.NoError(t, os.RemoveAll(dir)) }() file := writeTempFile(t, dir, `package main import ( - "time" "github.com/gofiber/fiber/v2/middleware/limiter" + "time" ) var _ = limiter.New(limiter.Config{ - Duration: time.Second, + Duration: time.Minute, Store: nil, - Key: func(c fiber.Ctx) string { return "" }, + Key: func(c fiber.Ctx) string { return "a" }, })`) var buf bytes.Buffer @@ -519,36 +604,39 @@ var _ = limiter.New(limiter.Config{ require.NoError(t, MigrateLimiterConfig(cmd, dir, nil, nil)) content := readFile(t, file) - assert.NotContains(t, content, "Duration:") assert.Contains(t, content, "Expiration:") - assert.NotContains(t, content, "Store:") assert.Contains(t, content, "Storage:") - assert.NotContains(t, content, "Key:") assert.Contains(t, content, "KeyGenerator:") assert.Contains(t, buf.String(), "Migrating limiter middleware configs") } -func Test_MigrateEnvVarConfig(t *testing.T) { +func Test_MigrateHealthcheckConfig(t *testing.T) { t.Parallel() - dir, err := os.MkdirTemp("", "menvvar") + dir, err := os.MkdirTemp("", "mhealth") require.NoError(t, err) defer func() { require.NoError(t, os.RemoveAll(dir)) }() file := writeTempFile(t, dir, `package main -import "github.com/gofiber/fiber/v2/middleware/envvar" -var _ = envvar.New(envvar.Config{ - ExportVars: []string{"A"}, - ExcludeVars: []string{"B"}, +import "github.com/gofiber/fiber/v2/middleware/healthcheck" +var _ = healthcheck.New(healthcheck.Config{ + LivenessProbe: func(c fiber.Ctx) bool { return true }, + LivenessEndpoint: "/live", + ReadinessProbe: func(c fiber.Ctx) bool { return true }, + ReadinessEndpoint: "/ready", })`) var buf bytes.Buffer cmd := newCmd(&buf) - require.NoError(t, MigrateEnvVarConfig(cmd, dir, nil, nil)) + require.NoError(t, MigrateHealthcheckConfig(cmd, dir, nil, nil)) content := readFile(t, file) - assert.NotContains(t, content, "ExcludeVars") - assert.Contains(t, buf.String(), "Migrating envvar middleware configs") + assert.Contains(t, content, "Probe:") + assert.NotContains(t, content, "LivenessProbe") + assert.NotContains(t, content, "ReadinessProbe") + assert.NotContains(t, content, "LivenessEndpoint") + assert.NotContains(t, content, "ReadinessEndpoint") + assert.Contains(t, buf.String(), "Migrating healthcheck middleware configs") } func Test_MigrateAppTestConfig(t *testing.T) { From e438e7dc4a020e875695dbfd3d1a880a7df2efa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9?= Date: Sun, 13 Jul 2025 00:06:08 +0200 Subject: [PATCH 27/27] add more migration methods --- cmd/internal/migrations/lists.go | 5 +- cmd/internal/migrations/v3/common.go | 54 ++++++++++++++ cmd/internal/migrations/v3/common_test.go | 85 +++++++++++++++++++++++ 3 files changed, 143 insertions(+), 1 deletion(-) diff --git a/cmd/internal/migrations/lists.go b/cmd/internal/migrations/lists.go index 0e35878..d6a4a34 100644 --- a/cmd/internal/migrations/lists.go +++ b/cmd/internal/migrations/lists.go @@ -39,20 +39,23 @@ var Migrations = []Migration{ v3migrations.MigrateStaticRoutes, v3migrations.MigrateTrustedProxyConfig, v3migrations.MigrateMount, - v3migrations.MigrateTrustedProxyConfig, v3migrations.MigrateConfigListenerFields, v3migrations.MigrateListenerCallbacks, + v3migrations.MigrateListenMethods, v3migrations.MigrateContextMethods, v3migrations.MigrateViewBind, v3migrations.MigrateCORSConfig, v3migrations.MigrateCSRFConfig, v3migrations.MigrateMonitorImport, + v3migrations.MigrateHealthcheckConfig, v3migrations.MigrateProxyTLSConfig, v3migrations.MigrateAppTestConfig, v3migrations.MigrateMiddlewareLocals, v3migrations.MigrateFilesystemMiddleware, v3migrations.MigrateLimiterConfig, v3migrations.MigrateEnvVarConfig, + v3migrations.MigrateSessionConfig, + v3migrations.MigrateReqHeaderParser, }, }, } diff --git a/cmd/internal/migrations/v3/common.go b/cmd/internal/migrations/v3/common.go index 898eab7..688dbbd 100644 --- a/cmd/internal/migrations/v3/common.go +++ b/cmd/internal/migrations/v3/common.go @@ -483,6 +483,23 @@ func MigrateLimiterConfig(cmd *cobra.Command, cwd string, _, _ *semver.Version) return nil } +// MigrateSessionConfig updates session middleware configuration fields +func MigrateSessionConfig(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { + err := internal.ChangeFileContent(cwd, func(content string) string { + reConfig := regexp.MustCompile(`session\.Config{[^}]*}`) + return reConfig.ReplaceAllStringFunc(content, func(s string) string { + s = strings.ReplaceAll(s, "Expiration:", "IdleTimeout:") + return s + }) + }) + if err != nil { + return fmt.Errorf("failed to migrate session configs: %w", err) + } + + cmd.Println("Migrating session middleware configs") + return nil +} + // MigrateAppTestConfig updates app.Test calls to use the new TestConfig parameter func MigrateAppTestConfig(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { err := internal.ChangeFileContent(cwd, func(content string) string { @@ -524,3 +541,40 @@ func MigrateMiddlewareLocals(cmd *cobra.Command, cwd string, _, _ *semver.Versio cmd.Println("Migrating middleware locals") return nil } + +// MigrateListenMethods replaces removed Listen helpers with Listen +func MigrateListenMethods(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { + replacer := strings.NewReplacer( + ".ListenTLSWithCertificate(", ".Listen(", + ".ListenTLS(", ".Listen(", + ".ListenMutualTLSWithCertificate(", ".Listen(", + ".ListenMutualTLS(", ".Listen(", + ) + + err := internal.ChangeFileContent(cwd, func(content string) string { + return replacer.Replace(content) + }) + if err != nil { + return fmt.Errorf("failed to migrate listen methods: %w", err) + } + + cmd.Println("Migrating listen methods") + return nil +} + +// MigrateReqHeaderParser replaces the deprecated ReqHeaderParser helper with the new binding API +func MigrateReqHeaderParser(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { + replacer := strings.NewReplacer( + ".ReqHeaderParser(", ".Bind().Header(", + ) + + err := internal.ChangeFileContent(cwd, func(content string) string { + return replacer.Replace(content) + }) + if err != nil { + return fmt.Errorf("failed to migrate ReqHeaderParser: %w", err) + } + + cmd.Println("Migrating request header parser helper") + return nil +} diff --git a/cmd/internal/migrations/v3/common_test.go b/cmd/internal/migrations/v3/common_test.go index 3cee65e..55c42ba 100644 --- a/cmd/internal/migrations/v3/common_test.go +++ b/cmd/internal/migrations/v3/common_test.go @@ -4,6 +4,7 @@ import ( "bytes" "os" "path/filepath" + "strings" "testing" "github.com/spf13/cobra" @@ -529,6 +530,40 @@ func main() { assert.Contains(t, buf.String(), "Migrating listener callbacks") } +func Test_MigrateListenMethods(t *testing.T) { + t.Parallel() + + dir, err := os.MkdirTemp("", "mlisten") + require.NoError(t, err) + defer func() { require.NoError(t, os.RemoveAll(dir)) }() + + file := writeTempFile(t, dir, `package main +import ( + "github.com/gofiber/fiber/v2" + "crypto/tls" +) +func main() { + app := fiber.New() + cert, _ := tls.LoadX509KeyPair("cert.pem", "key.pem") + app.ListenTLS(":443", "cert.pem", "key.pem") + app.ListenTLSWithCertificate(":443", cert) + app.ListenMutualTLS(":443", "cert.pem", "key.pem", "ca.pem") + app.ListenMutualTLSWithCertificate(":443", cert, "ca.pem") +}`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + require.NoError(t, MigrateListenMethods(cmd, dir, nil, nil)) + + content := readFile(t, file) + assert.NotContains(t, content, "ListenTLS(") + assert.NotContains(t, content, "ListenTLSWithCertificate(") + assert.NotContains(t, content, "ListenMutualTLS(") + assert.NotContains(t, content, "ListenMutualTLSWithCertificate(") + assert.Equal(t, 4, strings.Count(content, ".Listen(")) + assert.Contains(t, buf.String(), "Migrating listen methods") +} + func Test_MigrateFilesystemMiddleware(t *testing.T) { t.Parallel() @@ -690,3 +725,53 @@ func handler(c fiber.Ctx) error { assert.Contains(t, content, `requestid.FromContext(c)`) assert.Contains(t, buf.String(), "Migrating middleware locals") } + +func Test_MigrateReqHeaderParser(t *testing.T) { + t.Parallel() + + dir, err := os.MkdirTemp("", "mrhp") + 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 v any + c.ReqHeaderParser(&v) + return nil +}`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + require.NoError(t, MigrateReqHeaderParser(cmd, dir, nil, nil)) + + content := readFile(t, file) + assert.Contains(t, content, `.Bind().Header(&v)`) + assert.Contains(t, buf.String(), "Migrating request header parser helper") +} + +func Test_MigrateSessionConfig(t *testing.T) { + t.Parallel() + + dir, err := os.MkdirTemp("", "msession") + require.NoError(t, err) + defer func() { require.NoError(t, os.RemoveAll(dir)) }() + + file := writeTempFile(t, dir, `package main +import ( + "github.com/gofiber/fiber/v2/middleware/session" + "time" +) +var _ = session.New(session.Config{ + Expiration: 5 * time.Minute, +})`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + require.NoError(t, MigrateSessionConfig(cmd, dir, nil, nil)) + + content := readFile(t, file) + assert.Contains(t, content, "IdleTimeout:") + assert.NotContains(t, content, "Expiration:") + assert.Contains(t, buf.String(), "Migrating session middleware configs") +}