forked from gnolang/gno
-
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.
Signed-off-by: Norman Meier <norman@berty.tech>
- Loading branch information
Showing
5 changed files
with
317 additions
and
1 deletion.
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 |
---|---|---|
|
@@ -20,3 +20,4 @@ pbbindings.go | |
*# | ||
cover.out | ||
coverage.out | ||
/.deploy/ |
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,270 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"net/http" | ||
"os" | ||
"os/exec" | ||
"path/filepath" | ||
"regexp" | ||
"strconv" | ||
"strings" | ||
|
||
expect "github.com/Netflix/go-expect" | ||
"github.com/pkg/errors" | ||
) | ||
|
||
func bumpInPlace(packagesRoot, targetPkgPath, remoteGnoweb string) { | ||
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{} | ||
|
||
fmt.Println("\nFetching versions from remote...") | ||
|
||
roots := []string{targetPkgPath} | ||
seen := map[string]struct{}{} | ||
for len(roots) > 0 { | ||
pkgPath := roots[0] | ||
roots = roots[1:] | ||
if _, ok := seen[pkgPath]; ok { | ||
continue | ||
} | ||
seen[pkgPath] = struct{}{} | ||
roots = append(roots, requiredBy[pkgPath]...) | ||
|
||
// find highest version on remote | ||
nextVersion := 2 | ||
for { | ||
resp, err := http.Get(fmt.Sprintf("%s/%s_v%d/", remoteGnoweb, strings.TrimPrefix(pkgPath, "gno.land/"), nextVersion)) // last slash is important so we query sources and don't run into problems with render errors in realms | ||
if err != nil { | ||
panic(errors.Wrap(err, "failed to get "+pkgPath)) | ||
} | ||
if resp.StatusCode == 500 { | ||
break | ||
} | ||
if resp.StatusCode != 200 { | ||
panic("unexpected status code: " + strconv.Itoa(resp.StatusCode)) | ||
} | ||
nextVersion++ | ||
} | ||
|
||
newPkgPath := fmt.Sprintf("%s_v%d", pkgPath, nextVersion) | ||
upgrades[pkgPath] = newPkgPath | ||
} | ||
|
||
fmt.Print("Copying root to temporary directory...\n") | ||
tmpDir := ".deploy" | ||
if err := os.RemoveAll(tmpDir); err != nil { | ||
panic(errors.Wrap(err, "failed to remove "+tmpDir)) | ||
} | ||
cmd := exec.Command("cp", "-r", packagesRoot, tmpDir) | ||
cmd.Stderr = os.Stderr | ||
if err := cmd.Run(); err != nil { | ||
panic(errors.Wrap(err, "failed to copy "+packagesRoot)) | ||
} | ||
|
||
// preversedPackagesRoot := packagesRoot | ||
packagesRoot = tmpDir | ||
|
||
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 { | ||
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 { | ||
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 { | ||
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)) | ||
} | ||
} | ||
|
||
fmt.Print("\nDeploying:\n\n") | ||
|
||
// deploy packages in dependency order | ||
deployed := map[string]struct{}{} | ||
remaining := map[string]struct{}{} | ||
for pkgPath := range upgrades { | ||
remaining[pkgPath] = struct{}{} | ||
} | ||
for len(remaining) > 0 { | ||
leafs := map[string]struct{}{} | ||
for pkgPath := range remaining { | ||
deps := requires[pkgPath] | ||
if len(deps) == 0 { | ||
leafs[pkgPath] = struct{}{} | ||
} | ||
hasDep := false | ||
for _, dep := range deps { | ||
if _, ok := upgrades[dep]; ok { | ||
if _, ok := deployed[dep]; !ok { | ||
hasDep = true | ||
break | ||
} | ||
} | ||
} | ||
if !hasDep { | ||
leafs[pkgPath] = struct{}{} | ||
} | ||
} | ||
|
||
if len(leafs) == 0 { | ||
panic("no leafs found, probably a cylic dependency") | ||
} | ||
|
||
for leaf := range leafs { | ||
fmt.Println(upgrades[leaf]) | ||
c, err := expect.NewConsole() | ||
if err != nil { | ||
panic(errors.Wrap(err, "failed to create console")) | ||
} | ||
cmd := exec.Command("gnokey", "maketx", "addpkg", | ||
"-deposit=1ugnot", | ||
"-gas-fee=1ugnot", | ||
"-gas-wanted=10000000", | ||
"-broadcast=true", | ||
"-remote=testnet.gno.teritori.com:26657", | ||
"-chainid=teritori-1", | ||
"-pkgdir="+filepath.Join(packagesRoot, upgrades[leaf]), | ||
"-pkgpath="+upgrades[leaf], | ||
"onblock-test", | ||
) | ||
cmd.Stderr = c.Tty() | ||
cmd.Stdout = c.Tty() | ||
cmd.Stdin = c.Tty() | ||
if err := cmd.Start(); err != nil { | ||
panic(errors.Wrap(err, "failed to deploy "+upgrades[leaf])) | ||
} | ||
|
||
c.ExpectString("Enter password.") | ||
c.SendLine("") | ||
|
||
if err := cmd.Wait(); err != nil { | ||
panic(errors.Wrap(err, "failed to deploy "+upgrades[leaf])) | ||
} | ||
|
||
c.Close() | ||
|
||
deployed[leaf] = struct{}{} | ||
delete(remaining, leaf) | ||
} | ||
} | ||
} | ||
|
||
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 | ||
} |
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,39 @@ | ||
package main | ||
|
||
import ( | ||
"flag" | ||
"os" | ||
|
||
"github.com/peterbourgon/ff/v3" | ||
) | ||
|
||
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") | ||
remoteGnowebFlag = fs.String("remote", "https://testnet.gno.teritori.com", "remote gnoweb node") | ||
) | ||
|
||
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 | ||
|
||
if remoteGnowebFlag == nil || *remoteGnowebFlag == "" { | ||
panic("remote gnoweb node is required") | ||
} | ||
remoteGnoweb := *remoteGnowebFlag | ||
|
||
bumpInPlace(packagesRoot, targetPkgPath, remoteGnoweb) | ||
} |
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
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.