-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 055dbb0
Showing
15 changed files
with
988 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
/artifacts/ | ||
/.makefiles/ | ||
.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
|
||
-include .makefiles/Makefile | ||
-include .makefiles/pkg/go/v1/Makefile | ||
|
||
.makefiles/%: | ||
@curl -sfL https://makefiles.dev/v1 | bash /dev/stdin "$@" | ||
|
||
################################################################################ | ||
|
||
.PHONY: run | ||
run: $(GO_DEBUG_DIR)/gobump | ||
$< $(args) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package main | ||
|
||
import ( | ||
"flag" | ||
"fmt" | ||
"os" | ||
"os/exec" | ||
) | ||
|
||
func run() error { | ||
wd, err := os.Getwd() | ||
if err != nil { | ||
return fmt.Errorf("cannot determine current directory: %s", err) | ||
} | ||
|
||
var goTidy bool | ||
flag.BoolVar(&goTidy, "t", false, "run 'go mod tidy' command at the end of path update") | ||
flag.Usage = usage | ||
flag.Parse() | ||
|
||
modules, err := parseGoModFile(wd) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
newPath := flag.Arg(0) | ||
|
||
if err := checkModulePath(newPath, modules); err != nil { | ||
return err | ||
} | ||
|
||
if err := walkDir(wd, newPath); err != nil { | ||
return err | ||
} | ||
|
||
if goTidy { | ||
if err := runTidy(); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
fmt.Println("done") | ||
return nil | ||
} | ||
|
||
func runTidy() error { | ||
fmt.Println("running 'go mod tidy'...") | ||
|
||
out, err := exec.Command("go", "mod", "tidy").CombinedOutput() | ||
if err != nil { | ||
os.Stderr.Write(out) | ||
return fmt.Errorf("error running 'go mod tidy': %v", err) | ||
} | ||
|
||
os.Stdout.Write(out) | ||
return nil | ||
} | ||
|
||
func usage() { | ||
fmt.Fprintf(os.Stderr, "usage: gobump [flags] <new go module path>\n") | ||
flag.PrintDefaults() | ||
} | ||
|
||
func main() { | ||
if err := run(); err != nil { | ||
fmt.Printf("error: %s\n", err) | ||
os.Exit(1) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"io" | ||
"os" | ||
|
||
"github.com/danilvpetrov/gobump/transformers" | ||
) | ||
|
||
// runTransformers runs transformers against the file. If transformers performed | ||
// conflicting changes to the file, the last transformer always takes precedence. | ||
func runTransformers(file string, tt ...transformers.Transformer) error { | ||
f, err := os.OpenFile(file, os.O_RDWR, 0644) | ||
if err != nil { | ||
return err | ||
} | ||
defer f.Close() | ||
|
||
var buf bytes.Buffer | ||
for _, t := range tt { | ||
ok, err := t(f, &buf) | ||
if err != nil { | ||
return err | ||
} | ||
if !ok { | ||
continue | ||
} | ||
|
||
if err := f.Truncate(0); err != nil { | ||
return err | ||
} | ||
|
||
if _, err := f.Seek(0, io.SeekStart); err != nil { | ||
return err | ||
} | ||
if _, err := buf.WriteTo(f); err != nil { | ||
return err | ||
} | ||
|
||
if _, err := f.Seek(0, io.SeekStart); err != nil { | ||
return err | ||
} | ||
|
||
buf.Reset() | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
|
||
"golang.org/x/mod/modfile" | ||
"golang.org/x/mod/module" | ||
) | ||
|
||
// parseGoModFile parses "go.mod" file and returns the main module path and all | ||
// direct dependency module paths declared with "require" directive. | ||
// | ||
// For more info refer to this resource: https://go.dev/doc/modules/gomod-ref. | ||
func parseGoModFile(moduleDir string) ([]string, error) { | ||
gomodPath := filepath.Join(moduleDir, "go.mod") | ||
if fi, err := os.Stat(gomodPath); err != nil || fi.IsDir() { | ||
return nil, fmt.Errorf("directory %q is not a valid go module", moduleDir) | ||
} | ||
|
||
bb, err := os.ReadFile(gomodPath) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
mf, err := modfile.Parse(gomodPath, bb, nil) | ||
if err != nil { | ||
return nil, fmt.Errorf("invalid go.mod file: %s", err) | ||
} | ||
|
||
modules := []string{mf.Module.Mod.Path} | ||
for _, req := range mf.Require { | ||
if !req.Indirect { | ||
modules = append(modules, req.Mod.Path) | ||
} | ||
} | ||
|
||
return modules, nil | ||
} | ||
|
||
// checkModulePath checks the module path for its validity and relation to any | ||
// of the available module paths. | ||
func checkModulePath(modulePath string, availableModules []string) error { | ||
if err := module.CheckPath(modulePath); err != nil { | ||
return fmt.Errorf("invalid module path %q: %w", modulePath, err) | ||
} | ||
|
||
pnew, _, _ := module.SplitPathVersion(modulePath) | ||
for _, am := range availableModules { | ||
if pold, _, _ := module.SplitPathVersion(am); pold == pnew { | ||
return nil | ||
} | ||
} | ||
|
||
return fmt.Errorf( | ||
"failed to find a corresponding module for %q", | ||
modulePath, | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package main | ||
|
||
import ( | ||
"io/fs" | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
|
||
"github.com/danilvpetrov/gobump/transformers/gofile" | ||
"github.com/danilvpetrov/gobump/transformers/gomodfile" | ||
) | ||
|
||
// walkDir walks a given directory and runs the relevant file transformers that | ||
// apply the new go module path. | ||
func walkDir(dir, newPath string) error { | ||
return filepath.WalkDir( | ||
dir, | ||
func(path string, d fs.DirEntry, err error) error { | ||
// Do not walk into "testdata" directories. | ||
if d.IsDir() && d.Name() == "testdata" { | ||
return filepath.SkipDir | ||
} | ||
|
||
if d.IsDir() { | ||
return nil | ||
} | ||
|
||
// Ignore files starting with "." or "_". | ||
if strings.HasPrefix(d.Name(), ".") || strings.HasPrefix(d.Name(), "_") { | ||
return nil | ||
} | ||
|
||
if d.Type()&fs.ModeSymlink != 0 { | ||
// Ignore symlinks pointing to directories. | ||
if t, err := os.Stat(path); err == nil && t.IsDir() { | ||
return nil | ||
} | ||
} | ||
|
||
switch { | ||
default: | ||
return nil | ||
case filepath.Ext(path) == ".go": | ||
return runTransformers(path, gofile.UpdateImports(newPath)) | ||
case filepath.Base(path) == "go.mod": | ||
return runTransformers(path, gomodfile.UpdateModulePath(newPath)) | ||
} | ||
}, | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
module github.com/danilvpetrov/gobump | ||
|
||
go 1.20 | ||
|
||
require ( | ||
golang.org/x/mod v0.9.0 | ||
golang.org/x/tools v0.7.0 | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= | ||
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= | ||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= | ||
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= | ||
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package gofile | ||
|
||
import ( | ||
"go/ast" | ||
"go/format" | ||
"go/parser" | ||
"go/token" | ||
"io" | ||
"strconv" | ||
|
||
"github.com/danilvpetrov/gobump/transformers" | ||
"github.com/danilvpetrov/gobump/transformers/internal/pathx" | ||
"golang.org/x/tools/go/ast/astutil" | ||
) | ||
|
||
// UpdateImports replaces the import path in a .go file with a new import path | ||
// if the latter is applicable. | ||
func UpdateImports( | ||
newImportPath string, | ||
) transformers.Transformer { | ||
return func(in io.Reader, out io.Writer) (ok bool, err error) { | ||
fset := token.NewFileSet() | ||
f, err := parser.ParseFile(fset, "", in, parser.ParseComments) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
var rewrote bool | ||
for _, i := range f.Imports { | ||
p := importPath(i) | ||
np, ok, err := pathx.UpdateImportPath(newImportPath, p) | ||
if err != nil { | ||
return false, err | ||
} | ||
if ok { | ||
rewrote = astutil.RewriteImport(fset, f, p, np) | ||
} | ||
} | ||
if !rewrote { | ||
return false, nil | ||
} | ||
|
||
if err := format.Node(out, fset, f); err != nil { | ||
return false, err | ||
} | ||
|
||
return true, nil | ||
} | ||
} | ||
|
||
func importPath(i *ast.ImportSpec) string { | ||
p, err := strconv.Unquote(i.Path.Value) | ||
if err != nil { | ||
return "" | ||
} | ||
|
||
return p | ||
} |
Oops, something went wrong.