-
Notifications
You must be signed in to change notification settings - Fork 15
Add contrib v3 migration and update contrib tooling #222
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| package v3 | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "io/fs" | ||
| "os" | ||
| "path/filepath" | ||
| "regexp" | ||
| "strings" | ||
|
|
||
| semver "github.com/Masterminds/semver/v3" | ||
| "github.com/spf13/cobra" | ||
|
|
||
| "github.com/gofiber/cli/cmd/internal" | ||
| ) | ||
|
|
||
| var ( | ||
| contribImportRe = regexp.MustCompile(`(["\x60])github\.com/gofiber/contrib/([^"\x60\s]+)`) | ||
| contribModRe = regexp.MustCompile(`github\.com/gofiber/contrib/[^\s]+`) | ||
| ) | ||
|
|
||
| const ( | ||
| contribPrefix = "github.com/gofiber/contrib/" | ||
| contribV3Prefix = "github.com/gofiber/contrib/v3/" | ||
| ) | ||
|
|
||
| // MigrateContribPackages updates imports and module requirements that reference | ||
| // github.com/gofiber/contrib to use the v3 module path. | ||
| func MigrateContribPackages(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { | ||
| changedImports, err := internal.ChangeFileContent(cwd, func(content string) string { | ||
| return contribImportRe.ReplaceAllStringFunc(content, func(match string) string { | ||
| sub := contribImportRe.FindStringSubmatch(match) | ||
| if len(sub) != 3 { | ||
| return match | ||
| } | ||
| rest := sub[2] | ||
| if hasVersionPrefix(rest) { | ||
| return match | ||
| } | ||
| return sub[1] + contribV3Prefix + rest | ||
| }) | ||
| }) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to migrate contrib imports: %w", err) | ||
| } | ||
|
|
||
| modChanged := false | ||
| walkErr := filepath.WalkDir(cwd, func(path string, d fs.DirEntry, walkErr error) error { | ||
| if walkErr != nil { | ||
| return walkErr | ||
| } | ||
| if d.IsDir() { | ||
| if d.Name() == "vendor" { | ||
| return filepath.SkipDir | ||
| } | ||
| return nil | ||
| } | ||
| if d.Name() != "go.mod" { | ||
| return nil | ||
| } | ||
|
|
||
| b, err := os.ReadFile(path) // #nosec G304 -- reading module file | ||
| if err != nil { | ||
| return fmt.Errorf("read %s: %w", path, err) | ||
| } | ||
| content := string(b) | ||
| if !contribModRe.MatchString(content) { | ||
| return nil | ||
| } | ||
| updated := contribModRe.ReplaceAllStringFunc(content, func(match string) string { | ||
| rest := strings.TrimPrefix(match, contribPrefix) | ||
| if rest == match || hasVersionPrefix(rest) { | ||
| return match | ||
| } | ||
| return contribV3Prefix + rest | ||
| }) | ||
| if updated == content { | ||
| return nil | ||
| } | ||
| if err := os.WriteFile(path, []byte(updated), 0o600); err != nil { | ||
| return fmt.Errorf("write %s: %w", path, err) | ||
| } | ||
| modChanged = true | ||
| return nil | ||
| }) | ||
| if walkErr != nil { | ||
| return fmt.Errorf("failed to migrate contrib modules: %w", walkErr) | ||
| } | ||
|
|
||
| if !changedImports && !modChanged { | ||
| return nil | ||
| } | ||
|
|
||
| cmd.Println("Migrating contrib packages") | ||
| return nil | ||
| } | ||
|
|
||
| func hasVersionPrefix(rest string) bool { | ||
| if len(rest) < 2 || rest[0] != 'v' || rest[1] < '0' || rest[1] > '9' { | ||
| return false | ||
| } | ||
| i := 2 | ||
| for i < len(rest) && rest[i] >= '0' && rest[i] <= '9' { | ||
| i++ | ||
| } | ||
| if i == len(rest) { | ||
| return true | ||
| } | ||
| return rest[i] == '/' | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| package v3_test | ||
|
|
||
| import ( | ||
| "bytes" | ||
| "os" | ||
| "path/filepath" | ||
| "testing" | ||
|
|
||
| "github.com/stretchr/testify/assert" | ||
| "github.com/stretchr/testify/require" | ||
|
|
||
| "github.com/gofiber/cli/cmd/internal/migrations/v3" | ||
| ) | ||
|
|
||
| func Test_MigrateContribPackages(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| dir := t.TempDir() | ||
|
|
||
| file := writeTempFile(t, dir, `package main | ||
| import ( | ||
| session "github.com/gofiber/contrib/session" | ||
| ) | ||
|
|
||
| func main() { | ||
| _ = session.NewStore | ||
| }`) | ||
|
|
||
| modContent := `module example | ||
|
|
||
| go 1.22 | ||
|
|
||
| require ( | ||
| github.com/gofiber/fiber/v2 v2.0.0 | ||
| github.com/gofiber/contrib/session v1.2.3 | ||
| )` | ||
| require.NoError(t, os.WriteFile(filepath.Join(dir, "go.mod"), []byte(modContent), 0o600)) | ||
|
|
||
| var buf bytes.Buffer | ||
| cmd := newCmd(&buf) | ||
| require.NoError(t, v3.MigrateContribPackages(cmd, dir, nil, nil)) | ||
|
|
||
| content := readFile(t, file) | ||
| assert.Contains(t, content, "github.com/gofiber/contrib/v3/session") | ||
| assert.NotContains(t, content, "github.com/gofiber/contrib/session") | ||
|
|
||
| mod := readFile(t, filepath.Join(dir, "go.mod")) | ||
| assert.Contains(t, mod, "github.com/gofiber/contrib/v3/session v1.2.3") | ||
| assert.NotContains(t, mod, "github.com/gofiber/contrib/session v1.2.3") | ||
|
|
||
| assert.Contains(t, buf.String(), "Migrating contrib packages") | ||
| } | ||
|
|
||
| func Test_MigrateContribPackages_Replace(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| dir := t.TempDir() | ||
|
|
||
| file := writeTempFile(t, dir, `package main | ||
| import ( | ||
| websocket "github.com/gofiber/contrib/websocket" | ||
| ) | ||
|
|
||
| var _ = websocket.New`) // keep import | ||
|
|
||
| modContent := `module example | ||
|
|
||
| go 1.22 | ||
|
|
||
| require github.com/gofiber/contrib/websocket v1.0.0 | ||
|
|
||
| replace github.com/gofiber/contrib/websocket => ../local` | ||
| require.NoError(t, os.WriteFile(filepath.Join(dir, "go.mod"), []byte(modContent), 0o600)) | ||
|
|
||
| var buf bytes.Buffer | ||
| cmd := newCmd(&buf) | ||
| require.NoError(t, v3.MigrateContribPackages(cmd, dir, nil, nil)) | ||
|
|
||
| content := readFile(t, file) | ||
| assert.Contains(t, content, "github.com/gofiber/contrib/v3/websocket") | ||
|
|
||
| mod := readFile(t, filepath.Join(dir, "go.mod")) | ||
| assert.Contains(t, mod, "github.com/gofiber/contrib/v3/websocket v1.0.0") | ||
| assert.Contains(t, mod, "replace github.com/gofiber/contrib/v3/websocket => ../local") | ||
|
|
||
| assert.Contains(t, buf.String(), "Migrating contrib packages") | ||
| } | ||
|
|
||
| func Test_MigrateContribPackages_Idempotent(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| dir := t.TempDir() | ||
|
|
||
| file := writeTempFile(t, dir, `package main | ||
| import ( | ||
| "github.com/gofiber/contrib/v3/session" | ||
| ) | ||
|
|
||
| func main() { | ||
| _ = session.NewStore | ||
| }`) | ||
|
|
||
| modContent := `module example | ||
|
|
||
| go 1.22 | ||
|
|
||
| require github.com/gofiber/contrib/v3/session v1.2.3` | ||
| require.NoError(t, os.WriteFile(filepath.Join(dir, "go.mod"), []byte(modContent), 0o600)) | ||
|
|
||
| var buf bytes.Buffer | ||
| cmd := newCmd(&buf) | ||
| require.NoError(t, v3.MigrateContribPackages(cmd, dir, nil, nil)) | ||
| first := readFile(t, file) | ||
| firstMod := readFile(t, filepath.Join(dir, "go.mod")) | ||
|
|
||
| require.NoError(t, v3.MigrateContribPackages(cmd, dir, nil, nil)) | ||
| second := readFile(t, file) | ||
| secondMod := readFile(t, filepath.Join(dir, "go.mod")) | ||
|
|
||
| assert.Equal(t, first, second) | ||
| assert.Equal(t, firstMod, secondMod) | ||
| assert.Equal(t, "", buf.String()) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,6 +27,11 @@ var ( | |
|
|
||
| const vendorDir = "vendor" | ||
|
|
||
| const ( | ||
| contribModulePrefix = "github.com/gofiber/contrib/v3/" | ||
| contribRepoPrefix = "gofiber/contrib/v3" | ||
| ) | ||
|
Comment on lines
+30
to
+33
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The new Useful? React with 👍 / 👎. |
||
|
|
||
| func refreshContrib(cmd *cobra.Command, cwd, hash string) (bool, error) { | ||
| modules, err := findContribModules(cwd) | ||
| if err != nil { | ||
|
|
@@ -41,7 +46,7 @@ func refreshContrib(cmd *cobra.Command, cwd, hash string) (bool, error) { | |
| reader := bufio.NewReader(cmd.InOrStdin()) | ||
| for _, m := range modules { | ||
| latest := latestContribVersionFn(m) | ||
| prompt := fmt.Sprintf("Version for github.com/gofiber/contrib/%s (default %s): ", m, latest) | ||
| prompt := fmt.Sprintf("Version for %s%s (default %s): ", contribModulePrefix, m, latest) | ||
| cmd.Print(prompt) | ||
| line, err := reader.ReadString('\n') | ||
| if err != nil && err != io.EOF { | ||
|
|
@@ -65,7 +70,7 @@ func refreshContrib(cmd *cobra.Command, cwd, hash string) (bool, error) { | |
| if err != nil { | ||
| return false, fmt.Errorf("parse version: %w", err) | ||
| } | ||
| pv, err := pseudoVersionFromHash("gofiber/contrib", base, hash) | ||
| pv, err := pseudoVersionFromHash(contribRepoPrefix, base, hash) | ||
| if err != nil { | ||
| return false, fmt.Errorf("pseudo version: %w", err) | ||
| } | ||
|
|
@@ -87,7 +92,7 @@ func refreshContrib(cmd *cobra.Command, cwd, hash string) (bool, error) { | |
| return s | ||
| } | ||
| major := majorFromVersion(ver) | ||
| return fmt.Sprintf("\"github.com/gofiber/contrib/%s%s%s\"", mod, majorPath(major), rest) | ||
| return fmt.Sprintf("\"%s%s%s\"", contribModulePrefix+mod, majorPath(major), rest) | ||
| }) | ||
| }) | ||
| if err != nil { | ||
|
|
@@ -105,7 +110,7 @@ func refreshContrib(cmd *cobra.Command, cwd, hash string) (bool, error) { | |
| for mod, ver := range versions { | ||
| major := majorFromVersion(ver) | ||
| re := regexp.MustCompile(fmt.Sprintf(`(?m)^(\s*(?:require\s+)?)github.com/gofiber/contrib/(?:v\d+/)?%s(?:/v\d+)?\s+v[\w\.-]+`, regexp.QuoteMeta(mod))) | ||
| newLine := fmt.Sprintf(`${1}github.com/gofiber/contrib/%s%s %s`, mod, majorPath(major), ver) | ||
| newLine := fmt.Sprintf(`${1}%s%s %s`, contribModulePrefix+mod, majorPath(major), ver) | ||
| replaced := re.ReplaceAllString(content, newLine) | ||
| if replaced != content { | ||
| content = replaced | ||
|
|
@@ -165,7 +170,7 @@ func findContribModules(cwd string) ([]string, error) { | |
| func latestContribVersion(module string) string { | ||
| ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) | ||
| defer cancel() | ||
| url := fmt.Sprintf("https://proxy.golang.org/github.com/gofiber/contrib/%s/@latest", module) | ||
| url := fmt.Sprintf("https://proxy.golang.org/%s%s/@latest", contribModulePrefix, module) | ||
| b, status, err := cachedGET(ctx, url, nil) | ||
| if err != nil || status != 200 { | ||
| return "" | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When updating the
go.modfile, the original file permissions are not preserved becauseos.WriteFileis called with a hardcoded mode0o600. This could cause unexpected permission changes in the user's project. It's better to read the original file's permissions and use them when writing the updated file.