Allow forced v3 migration with existing v3 usage#163
Conversation
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. Warning Rate limit exceeded@ReneWerner87 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 26 minutes and 14 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (3)
WalkthroughImplements regex-based Go import and go.mod migrations for Fiber version upgrades, adjusts migrate command to support forced migrations by deriving a prior-major from-version, and updates/expands tests to cover forced v2→v3 migration and output expectations. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant U as User
participant CLI as migrate cmd
participant MV as migrateRunE
participant DM as DoMigration
participant GP as MigrateGoPkgs
U->>CLI: run migrate [-t x.y.z] [--force]
CLI->>MV: parse flags, resolve versions
alt --force and target ≤ current
MV->>MV: derive migrateFrom = (target.Major-1).0.0
else normal
MV->>MV: migrateFrom = currentVersion
end
MV->>DM: DoMigration(migrateFrom, targetVersion)
DM->>GP: MigrateGoPkgs(..., _ , targetVersion)
note right of GP: Regex-update imports github.com/gofiber/fiber/v<maj><br/>Regex-update go.mod requirement/version
GP-->>DM: done
DM-->>CLI: migration complete
CLI-->>U: prints from→to and "Migrating Go packages"
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Poem
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Summary of Changes
Hello @ReneWerner87, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!
This pull request enhances the Fiber CLI's migration tool, specifically improving its ability to handle forced migrations to a target major version, such as Fiber v3. It ensures that all necessary migration steps are executed even if the project already has partial usage of the target version, by treating the source as the previous major version for migration purposes. This allows for more flexible and robust migration paths, especially in complex project setups.
Highlights
- Broadened Go Package Migration: The MigrateGoPkgs function now uses a more general regular expression to replace any github.com/gofiber/fiber/vX import with the specified target major version, making the package migration more robust.
- Improved Forced Migration Logic: When a migration is forced, the tool now intelligently determines the effective "from" version for the migration. If the target version is not strictly greater than the current version, it assumes the migration should run from the previous major version to ensure all relevant migration steps are applied.
- Comprehensive Test Coverage for Forced Migrations: New test cases have been added to validate the forced migration behavior, particularly in scenarios where a project might already have some components on the target major version (e.g., go.mod updated) while other parts still reference an older major version (e.g., source code imports).
Using Gemini Code Assist
The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.
Invoking Gemini
You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.
| Feature | Command | Description |
|---|---|---|
| Code Review | /gemini review |
Performs a code review for the current pull request in its current state. |
| Pull Request Summary | /gemini summary |
Provides a summary of the current pull request in its current state. |
| Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in issue comments and review comments. |
| Help | /gemini help |
Displays a list of available commands. |
Customization
To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.
Limitations & Feedback
Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.
You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.
Footnotes
-
Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩
There was a problem hiding this comment.
Code Review
This pull request correctly implements the forced migration feature, allowing migrations to run even if the project is already on the target version by treating the source as the previous major version. The changes in cmd/internal/migrations/common.go to use a regex for package replacement make the migration more robust. The new tests, especially Test_Migrate_ForcePartialV3, are well-designed and cover the new functionality effectively.
I've added a few comments with suggestions for improvement, mainly concerning code style consistency (indentation) and enhancing user-facing error messages for better clarity. Overall, the logic is sound and the changes are a good addition.
| if !targetVersion.GreaterThan(currentVersion) && !(opts.Force && targetVersion.Equal(currentVersion)) { | ||
| return fmt.Errorf("target version v%s is not greater than current version v%s", opts.TargetVersionS, currentVersionS) | ||
| } | ||
|
|
||
| wd, err := os.Getwd() | ||
| if err != nil { | ||
| return fmt.Errorf("cannot get current working directory: %w", err) | ||
| } | ||
|
|
||
| migrateFrom := currentVersion | ||
| migrateFromS := currentVersionS | ||
| if opts.Force && !targetVersion.GreaterThan(currentVersion) { | ||
| prevMajor := targetVersion.Major() - 1 | ||
| migrateFrom, err = semver.NewVersion(fmt.Sprintf("%d.0.0", prevMajor)) | ||
| if err != nil { | ||
| return fmt.Errorf("invalid previous major version %d: %w", prevMajor, err) | ||
| } | ||
| migrateFromS = migrateFrom.String() | ||
| } | ||
|
|
||
| err = migrations.DoMigration(cmd, wd, migrateFrom, targetVersion) | ||
| if err != nil { | ||
| return fmt.Errorf("migration failed %w", err) | ||
| } | ||
|
|
||
| if !opts.SkipGoMod { | ||
| if err := runGoMod(wd); err != nil { | ||
| return fmt.Errorf("go mod: %w", err) | ||
| } | ||
| } | ||
|
|
||
| msg := fmt.Sprintf("Migration from Fiber %s to %s", migrateFromS, opts.TargetVersionS) | ||
| cmd.Println(termenv.String(msg). | ||
| Foreground(termenv.ANSIBrightBlue)) |
| migrateFrom := currentVersion | ||
| migrateFromS := currentVersionS | ||
| if opts.Force && !targetVersion.GreaterThan(currentVersion) { | ||
| prevMajor := targetVersion.Major() - 1 |
There was a problem hiding this comment.
While the logic correctly handles an invalid prevMajor by returning an error, the resulting error message (e.g., invalid previous major version -1: ...) might not be very intuitive for the user. It would be more user-friendly to explicitly check if the target version's major component is less than 1 and return a more descriptive error message. This prevents attempting to create a version with a negative major number and provides clearer feedback.
if targetVersion.Major() < 1 {
return fmt.Errorf("forced migration is only supported for target versions with major version 1 or higher")
}
prevMajor := targetVersion.Major() - 1There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
cmd/migrate_test.go (1)
165-183: Fix gofmt formatting in subtest "force".This block is indented with spaces and fails gofmt (CI failure). Convert to standard Go formatting.
Apply this diff:
- t.Run("force", func(t *testing.T) { - origExec := execCommand - var cmds []*exec.Cmd + t.Run("force", func(t *testing.T) { + origExec := execCommand + var cmds []*exec.Cmd execCommand = func(name string, args ...string) *exec.Cmd { cs := append([]string{"-test.run=TestHelperProcess", "--", name}, args...) cmd := exec.Command(os.Args[0], cs...) // #nosec G204 -- safe for test cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"} cmds = append(cmds, cmd) return cmd } defer func() { execCommand = origExec }() - cmd := newMigrateCmd() - out, err := runCobraCmd(cmd, "-t=3.0.0", "-f") - require.NoError(t, err) - assert.Contains(t, out, "Migration from Fiber 2.0.0 to 3.0.0") - assert.Contains(t, out, "Migrating Go packages") - assert.Len(t, cmds, 3) - }) + cmd := newMigrateCmd() + out, err := runCobraCmd(cmd, "-t=3.0.0", "-f") + require.NoError(t, err) + assert.Contains(t, out, "Migration from Fiber 2.0.0 to 3.0.0") + assert.Contains(t, out, "Migrating Go packages") + assert.Len(t, cmds, 3) + })
🧹 Nitpick comments (6)
cmd/internal/migrations/common.go (4)
15-18: Anchor and tighten regexes to avoid over-matching; address CodeQL warning.The current patterns can match unintended content (e.g., comments, arbitrary substrings) and are flagged by CodeQL for missing anchors. Anchoring the go.mod regex to require-lines and using word boundaries for import paths will reduce false positives while keeping behavior.
Apply this diff:
var ( - pkgRegex = regexp.MustCompile(`(github\.com\/gofiber\/fiber\/)(v\d+)( *?)(v[\w.-]+)`) - pkgImportRegex = regexp.MustCompile(`github\.com/gofiber/fiber/v\d+`) + // Anchor to 'require' lines in go.mod; multiline mode ensures ^/$ are per-line. + pkgRegex = regexp.MustCompile(`(?m)^(\s*require\s+github\.com/gofiber/fiber/)(v\d+)(\s+)(v[\w.-]+)\s*$`) + // Constrain replacement to the version path segment using word boundaries. + pkgImportRegex = regexp.MustCompile(`\bgithub\.com/gofiber/fiber/v\d+\b`) )
20-23: Limit content rewrites to import specs (AST-based) to avoid touching comments/strings.Even with a narrowed regex, raw string replacement can still modify comments or unrelated string literals in .go files. Using go/parser to walk ImportSpecs and rewrite only those starting with github.com/gofiber/fiber/v is safer and future-proof. If you want, I can provide a small helper using go/ast to do this.
35-39: go.mod replacement pattern misses replace directives and indirect blocks; consider modfile.Regex on go.mod is brittle. Using golang.org/x/mod/modfile to parse and update the module requirement guarantees correct handling for: require blocks, indirects, replace directives, and formatting.
I can draft a modfile-based implementation if you want to harden this step.
41-44: Preserve or use standard permissions when writing go.mod (avoid 0600).Writing go.mod with 0600 can be surprising in shared environments. Either preserve the existing permissions or default to 0644.
Apply this diff:
- if err := os.WriteFile(modFile, []byte(fileContentStr), 0o600); err != nil { + fi, statErr := os.Stat(modFile) + mode := os.FileMode(0o644) + if statErr == nil { + mode = fi.Mode().Perm() + } + if err := os.WriteFile(modFile, []byte(fileContentStr), mode); err != nil { return fmt.Errorf("write %s: %w", modFile, err) }cmd/migrate_test.go (1)
206-238: Add an assertion to validate go.mod remains on v3 after forced partial migration.This test proves import rewrites; adding a go.mod assertion tightens coverage of the v3 state when forcing.
Apply this diff:
out, err := runCobraCmd(cmd, "-t=3.0.0", "-f") require.NoError(t, err) assert.Contains(t, out, "Migration from Fiber 2.0.0 to 3.0.0") assert.Contains(t, out, "Migrating Go packages") + gm := readFileTB(t, filepath.Join(dir, "go.mod")) + assert.Contains(t, gm, "github.com/gofiber/fiber/v3 v3.0.0") + content := readFileTB(t, filepath.Join(dir, "main.go")) assert.Contains(t, content, "github.com/gofiber/fiber/v3") assert.NotContains(t, content, "github.com/gofiber/fiber/v2")cmd/migrate.go (1)
81-90: Guard against invalid previous-major computation for edge versions.Computing prevMajor can go negative (e.g., target 0.x.y). Add an explicit check for clarity; you currently rely on semver.NewVersion to fail later.
Apply this diff:
if opts.Force && !targetVersion.GreaterThan(currentVersion) { - prevMajor := targetVersion.Major() - 1 + prevMajor := targetVersion.Major() - 1 + if prevMajor < 0 { + return fmt.Errorf("cannot derive previous major from %s", targetVersion) + } migrateFrom, err = semver.NewVersion(fmt.Sprintf("%d.0.0", prevMajor))
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
cmd/internal/migrations/common.go(1 hunks)cmd/migrate.go(1 hunks)cmd/migrate_test.go(3 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: ReneWerner87
PR: gofiber/fiber#3161
File: app.go:923-932
Timestamp: 2024-11-15T07:56:21.623Z
Learning: In the Fiber framework, breaking changes are acceptable when moving from version 2 to version 3, including modifications to method signatures such as in the `Test` method in `app.go`.
📚 Learning: 2024-11-15T07:56:21.623Z
Learnt from: ReneWerner87
PR: gofiber/fiber#3161
File: app.go:923-932
Timestamp: 2024-11-15T07:56:21.623Z
Learning: In the Fiber framework, breaking changes are acceptable when moving from version 2 to version 3, including modifications to method signatures such as in the `Test` method in `app.go`.
Applied to files:
cmd/migrate_test.go
🧬 Code graph analysis (2)
cmd/migrate.go (1)
cmd/internal/migrations/lists.go (1)
DoMigration(73-96)
cmd/internal/migrations/common.go (1)
cmd/internal/helpers.go (1)
ChangeFileContent(36-68)
🪛 GitHub Check: lint
cmd/migrate.go
[failure] 72-72:
File is not properly formatted (gofmt)
cmd/internal/migrations/common.go
[failure] 20-20:
File is not properly formatted (gofumpt)
cmd/migrate_test.go
[failure] 165-165:
File is not properly formatted (gofmt)
🪛 GitHub Check: CodeQL
cmd/internal/migrations/common.go
[failure] 17-17: Missing regular expression anchor
When this is used as a regular expression on a URL, it may match anywhere, and arbitrary hosts may come before or after it.
🪛 GitHub Actions: golangci-lint
cmd/internal/migrations/common.go
[error] 20-20: golangci-lint run: File is not properly formatted (gofumpt).
🔇 Additional comments (3)
cmd/internal/migrations/common.go (1)
20-20: Manual Verification Required: Ensure gofumpt/gofmt FormattingI attempted to run the suggested formatting commands but the tools weren’t available in this environment:
gofumpt -l -w . gofmt -s -w .Please install and run these locally to confirm and fix any formatting issues reported by CI.
• Install or verify
gofumptandgofmtare in your PATH
• From the repo root, run:go install mvdan.cc/gofumpt@latest # if gofumpt is missing gofumpt -l -w . gofmt -s -w .• Re-commit any changes and ensure CI passes
cmd/migrate_test.go (1)
165-165: Ensure gofmt formatting on cmd/migrate_test.go
- CI reported a gofmt lint failure in
cmd/migrate_test.goat thet.Run("force", …)block.- Please run
locally, commit the updated file, and re-trigger CI to confirm the formatting issues are resolved.gofmt -s -w cmd/migrate_test.go- Note: the sandbox lacked
gofmt, so manual verification is needed to ensure consistency with CI.cmd/migrate.go (1)
72-72: Formatting tools unavailable for automated verificationIt appears that neither
gofumptnorgofmtare installed in this environment, so the formatting check could not be performed automatically. Please verify locally thatcmd/migrate.gois properly formatted and that CI’s lint step will pass:• Install the formatter tools:
go install mvdan.cc/gofumpt@latest• Run them on the file:
gofumpt -l -w cmd/migrate.go gofmt -s -w cmd/migrate.go• Commit any changes and ensure the CI formatting check succeeds.
| if !targetVersion.GreaterThan(currentVersion) && !(opts.Force && targetVersion.Equal(currentVersion)) { | ||
| return fmt.Errorf("target version v%s is not greater than current version v%s", opts.TargetVersionS, currentVersionS) | ||
| } | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
Broaden forced migration allowance to any same-major target, not only exact equality.
Current gate allows force only when target == current. The PR goal is to “allow forced v3 migration with existing v3 usage,” which also covers cases like current 3.2.x → target 3.0.0. Relaxing to same major keeps safety (still blocks downgrades across majors) but enables the intended behavior.
Apply this diff:
- if !targetVersion.GreaterThan(currentVersion) && !(opts.Force && targetVersion.Equal(currentVersion)) {
+ if !targetVersion.GreaterThan(currentVersion) && !(opts.Force && targetVersion.Major() == currentVersion.Major()) {
return fmt.Errorf("target version v%s is not greater than current version v%s", opts.TargetVersionS, currentVersionS)
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if !targetVersion.GreaterThan(currentVersion) && !(opts.Force && targetVersion.Equal(currentVersion)) { | |
| return fmt.Errorf("target version v%s is not greater than current version v%s", opts.TargetVersionS, currentVersionS) | |
| } | |
| if !targetVersion.GreaterThan(currentVersion) && !(opts.Force && targetVersion.Major() == currentVersion.Major()) { | |
| return fmt.Errorf("target version v%s is not greater than current version v%s", opts.TargetVersionS, currentVersionS) | |
| } |
🧰 Tools
🪛 GitHub Check: lint
[failure] 72-72:
File is not properly formatted (gofmt)
🤖 Prompt for AI Agents
In cmd/migrate.go around lines 72 to 75, the current check only allows force
when target == current; change it to allow force when the target and current
share the same major version (e.g., current 3.x → target 3.0.0) so same-major
forced migrations are permitted while cross-major downgrades remain blocked.
Replace the condition so it returns an error only if targetVersion is not
greaterThan currentVersion and not (opts.Force && target and current have the
same major number); implement the same-major check by comparing the major
component of targetVersion and currentVersion (e.g., targetVersion.Major() ==
currentVersion.Major()) and keep the existing error text and variables.
Summary
Testing
go test ./...https://chatgpt.com/codex/tasks/task_e_68a8bbf114788326bbf4d39e00793d3e
Summary by CodeRabbit
New Features
Tests