Skip to content

Commit

Permalink
feat: bumpkg cmd
Browse files Browse the repository at this point in the history
Signed-off-by: Norman Meier <norman@berty.tech>
  • Loading branch information
n0izn0iz committed Aug 22, 2023
1 parent b40ac9c commit 3ae79f0
Showing 1 changed file with 205 additions and 0 deletions.
205 changes: 205 additions & 0 deletions gno.land/cmd/bumpkg/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
package main

import (
"flag"
"fmt"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"

"github.com/peterbourgon/ff/v3"
"github.com/pkg/errors"
)

func main() {
fs := flag.NewFlagSet("bumpkg", flag.ContinueOnError)
var (
packagesRootFlag = fs.String("root", "examples", "root directory of packages")
targetPkgPathFlag = fs.String("target", "", "target package path")
)

err := ff.Parse(fs, os.Args[1:])
if err != nil {
panic(err)
}

if targetPkgPathFlag == nil || *targetPkgPathFlag == "" {
panic("target package path is required")
}
targetPkgPath := *targetPkgPathFlag

if packagesRootFlag == nil || *packagesRootFlag == "" {
panic("packages root is required")
}
packagesRoot := *packagesRootFlag

targetPackageFSPath := filepath.Join(packagesRoot, targetPkgPath)
targetPackageGnoModPath := filepath.Join(targetPackageFSPath, "gno.mod")
fmt.Println("Target package:\n\n" + targetPkgPath)

allGnoMods := map[string]struct{}{}
if err := filepath.Walk(packagesRoot, func(path string, info os.FileInfo, err error) error {
if err != nil {
fmt.Println("error during walk:", err)
return nil
}
if info.IsDir() || info.Name() != "gno.mod" {
return nil
}

allGnoMods[path] = struct{}{}

return nil
}); err != nil {
panic(errors.Wrap(err, "failed to walk packages"))
}
if _, ok := allGnoMods[targetPackageGnoModPath]; !ok {
panic("target package not found")
}

requires := map[string][]string{}
requiredBy := map[string][]string{}
for gnoModPath := range allGnoMods {
deps, err := gnoModDeps(gnoModPath)
if err != nil {
panic(errors.Wrap(err, "failed to parse "+gnoModPath))
}

pkgPath := strings.TrimSuffix(strings.TrimPrefix(gnoModPath, packagesRoot+"/"), "/gno.mod") // FIXME: brittle, not cross-platform

requires[pkgPath] = deps
for _, dep := range deps {
requiredBy[dep] = append(requiredBy[dep], pkgPath)
}
}

upgrades := map[string]string{}

roots := []string{targetPkgPath}
seen := map[string]struct{}{}
for len(roots) > 0 {
root := roots[0]
roots = roots[1:]
if _, ok := seen[root]; ok {
continue
}
seen[root] = struct{}{}
roots = append(roots, requiredBy[root]...)

vR := regexp.MustCompile(`_v(\d+)$`)
submatches := vR.FindAllStringSubmatch(root, -1)
version := uint64(1)
unversioned := true
if len(submatches) >= 1 && len(submatches[0]) >= 2 {
vMatch := submatches[0][1]
v, err := strconv.ParseUint(vMatch, 10, 64)
if err != nil {
panic("failed to parse version")
}
version = v
unversioned = false
}
nextVersion := version + 1
basePkgPath := root
if !unversioned {
basePkgPath = strings.TrimSuffix(basePkgPath, "_v"+strconv.FormatUint(version, 10))
}
newPkgPath := fmt.Sprintf("%s_v%d", basePkgPath, nextVersion)
upgrades[root] = newPkgPath
}

fmt.Print("\nBumping:\n\n")

for oldPkgPath, newPkgPath := range upgrades {
fmt.Println(oldPkgPath, "->", newPkgPath)

r := regexp.MustCompile(oldPkgPath)

// change module name in gno.mod
gnoModPath := filepath.Join(packagesRoot, oldPkgPath, "gno.mod")
data, err := os.ReadFile(gnoModPath)
if err != nil {
panic(errors.Wrap(err, "failed to read "+gnoModPath))
}
edited := r.ReplaceAll(data, []byte(newPkgPath))
if err := os.WriteFile(gnoModPath, edited, 0644); err != nil {

Check failure on line 127 in gno.land/cmd/bumpkg/main.go

View workflow job for this annotation

GitHub Actions / lint

File is not `gofumpt`-ed (gofumpt)
panic(errors.Wrap(err, "failed to write "+gnoModPath))
}

for _, child := range requiredBy[oldPkgPath] {
// change import paths in dependent .gno files
if err := filepath.Walk(filepath.Join(packagesRoot, child), func(path string, info os.FileInfo, err error) error {
if err != nil {
fmt.Println("error during walk:", err)
return nil
}

if info.IsDir() || !strings.HasSuffix(path, ".gno") {
return nil
}

// replace oldPkgPath with newPkgPath in file
data, err := os.ReadFile(path)
if err != nil {
return errors.Wrap(err, "failed to read "+path)
}
edited := r.ReplaceAll(data, []byte(newPkgPath))
if err := os.WriteFile(path, edited, 0644); err != nil {

Check failure on line 149 in gno.land/cmd/bumpkg/main.go

View workflow job for this annotation

GitHub Actions / lint

File is not `gofumpt`-ed (gofumpt)
return errors.Wrap(err, "failed to write "+path)
}

return nil
}); err != nil {
panic(errors.Wrap(err, "failed to walk packages"))
}

// change import paths in dependent gno.mod files
gnoModPath := filepath.Join(packagesRoot, child, "gno.mod")
data, err := os.ReadFile(gnoModPath)
if err != nil {
panic(errors.Wrap(err, "failed to read "+gnoModPath))
}
edited := r.ReplaceAll(data, []byte(newPkgPath))
if err := os.WriteFile(gnoModPath, edited, 0644); err != nil {

Check failure on line 165 in gno.land/cmd/bumpkg/main.go

View workflow job for this annotation

GitHub Actions / lint

File is not `gofumpt`-ed (gofumpt)
panic(errors.Wrap(err, "failed to write "+gnoModPath))
}
}
}

for oldPkgPath, newPkgPath := range upgrades {
// rename directory
if err := os.Rename(filepath.Join(packagesRoot, oldPkgPath), filepath.Join(packagesRoot, newPkgPath)); err != nil {
panic(errors.Wrap(err, "failed to rename "+oldPkgPath))
}
}
}

func gnoModDeps(gnoModPath string) ([]string, error) {
data, err := os.ReadFile(gnoModPath)
if err != nil {
return nil, errors.Wrap(err, "failed to read "+gnoModPath)
}
r := regexp.MustCompile(`(?s)require.+?\((.+?)\)`)
submatches := r.FindAllStringSubmatch(string(data), -1)
if len(submatches) < 1 || len(submatches[0]) < 2 {
return nil, nil
}
lines := strings.Split(submatches[0][1], "\n")
depEntries := []string{}
for _, line := range lines {
line = strings.TrimSpace(line)
if line == "" {
continue
}
depR := regexp.MustCompile(`"(.+)"`)
submatches := depR.FindAllStringSubmatch(line, -1)
if len(submatches) < 1 || len(submatches[0]) < 2 {
return nil, fmt.Errorf("failed to parse dep line: %q", line)
}
depEntry := submatches[0][1]
depEntries = append(depEntries, depEntry)
}
return depEntries, nil
}

0 comments on commit 3ae79f0

Please sign in to comment.