Skip to content

Commit

Permalink
base: local-ic testnet generator (#16)
Browse files Browse the repository at this point in the history
* local-ic json generator

* localic: download or shim run through spawn

* get relative paths for local-ic + set ICTEST_HOME default

* Initialized with Ignite CLI

* initial commit

* rm test

* git init

* touch ups

---------

Co-authored-by: Developer Experience team at Ignite <hello@ignite.com>
  • Loading branch information
Reecepbcups and Developer Experience team at Ignite authored Feb 10, 2024
1 parent fc5c322 commit ea23fca
Show file tree
Hide file tree
Showing 15 changed files with 366 additions and 51 deletions.
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
local-ic
/local-ic
/bin/local-ic
heighliner/
interchaintest/
dist/

**/configs/logs.json
my-project/
49 changes: 36 additions & 13 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,52 @@

CWD := $(dir $(abspath $(firstword $(MAKEFILE_LIST))))

# ldflags = -X main.MakeFileInstallDirectory=$(CWD)
# ldflags := $(strip $(ldflags))
# BUILD_FLAGS := -ldflags '$(ldflags)'
DATE := $(shell date '+%Y-%m-%dT%H:%M:%S')
HEAD = $(shell git rev-parse HEAD)
LD_FLAGS =
BUILD_FLAGS = -mod=readonly -ldflags='$(LD_FLAGS)'

.PHONY: build
## install: Install the binary.
install:
@echo ⏳ Installing Spawn...
go install $(BUILD_FLAGS) ./cmd/spawn
@echo ✅ Spawn installed to $(shell which spawn)

## build: Build to ./bin/spawn.
build:
go build $(BUILD_FLAGS) -o ./bin/spawn ./cmd/spawn

.PHONY: run
## run: Run the raw source.
run:
go run ./cmd/spawn $(filter-out $@,$(MAKECMDGOALS))

.PHONY: install
install:
go install $(BUILD_FLAGS) ./cmd/spawn

.PHONY: install build run

###############################################################################
### heighliner ###
###############################################################################
# ---- Downloads ----

## get-heighliner: Install the cosmos docker utility.
get-heighliner:
@echo ⏳ Installing heighliner...
git clone https://github.com/strangelove-ventures/heighliner.git
cd heighliner && go install
@echo ✅ heighliner installed to $(shell which heighliner)

## get-localic: Install local interchain testnet manager.
get-localic:
@echo ⏳ Installing local-interchain...
git clone --branch v8.1.0 https://github.com/strangelove-ventures/interchaintest.git
cd interchaintest/local-interchain && make install
@echo ✅ local-interchain installed to $(shell which local-ic)

.PHONY: get-heighliner

help: Makefile
@echo
@echo " Choose a command run in "spawn", or just run 'make' for install"
@echo
@sed -n 's/^##//p' $< | column -t -s ':' | sed -e 's/^/ /'
@echo

.PHONY: help

.PHONY: get-heighliner
.DEFAULT_GOAL := install
122 changes: 106 additions & 16 deletions cmd/spawn/download-localic.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,96 @@ import (
"io"
"net/http"
"os"
"os/exec"
"path"
"strings"
"time"

"github.com/schollz/progressbar/v3"
"github.com/spf13/cobra"
)

const LocalICURL = "https://github.com/strangelove-ventures/interchaintest/releases/download/v8.0.0/local-ic"
const DefaultVersion = "v8.1.0"

var DownloadLocalIC = &cobra.Command{
Use: "download",
Short: fmt.Sprintf("Download LocalInterchain from %s", LocalICURL),
Args: cobra.ExactArgs(0),
var LocalICURL = "https://github.com/strangelove-ventures/interchaintest/releases/download/" + DefaultVersion + "/local-ic"

const (
FlagVersionOverride = "version"
FlagForceDownload = "download"
FlagLocationPath = "print-location"
)

func init() {
LocalICCmd.Flags().String(FlagVersionOverride, DefaultVersion, "change the local-ic version to use")
LocalICCmd.Flags().Bool(FlagForceDownload, false, "force download of local-ic")
LocalICCmd.Flags().Bool(FlagLocationPath, false, "print the location of local-ic binary")
}

// ---
// make install && ICTEST_HOME=./simapp spawn local-ic start testnet
// make install && cd simapp && spawn local-ic start testnet
// ---
// TODO: Do something like `curl https://get.ignite.com/cli! | bash`? just with windows support
var LocalICCmd = &cobra.Command{
Use: "local-ic",
Short: "Local Interchain",
Long: fmt.Sprintf("Download Local Interchain from %s", LocalICURL),
// Args: cobra.
Run: func(cmd *cobra.Command, args []string) {
downloadBin()
version, _ := cmd.Flags().GetString(FlagVersionOverride)
forceDownload, _ := cmd.Flags().GetBool(FlagForceDownload)
debugBinaryLoc, _ := cmd.Flags().GetBool(FlagLocationPath)

loc := whereIsLocalICInstalled()
if (forceDownload || loc == "") && version != "" {
downloadBin(version)
loc = whereIsLocalICInstalled()
}

if debugBinaryLoc {
fmt.Println(loc)
return
}

if err := os.Chmod(loc, 0755); err != nil {
fmt.Println("Error setting local-ic permissions:", err)
}

// set to use the current dir if it is not overrriden
if os.Getenv("ICTEST_HOME") == "" {
if err := os.Setenv("ICTEST_HOME", "."); err != nil {
fmt.Println("Error setting ICTEST_HOME:", err)
}
}

if err := execCommand(loc, args...); err != nil {
fmt.Println("Error calling local-ic:", err)
}
},
}

// TODO: Download & move to the users GOPATH automatically? (keep a copy in a build/ folder?)
func downloadBin() error {
func whereIsLocalICInstalled() string {
for _, path := range []string{"local-ic", "bin/local-ic", "local-interchain/localic"} {
if _, err := os.Stat(path); err == nil {
return path
}
}

if path, err := exec.LookPath("local-ic"); err == nil {
return path
}

return ""
}

func execCommand(command string, args ...string) error {
cmd := exec.Command(command, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}

func downloadBin(version string) error {
file := "local-ic"

f, err := os.Create(file)
Expand All @@ -32,22 +103,35 @@ func downloadBin() error {
}
defer f.Close()

fmt.Printf("Downloading Local Interchain binary...")

currentDir, err := os.Getwd()
if err != nil {
return err
}

downloadWithProgress(path.Join(currentDir, file), LocalICURL)
if version != "" && version != DefaultVersion {
if version[0] != 'v' {
version = "v" + version
}

// Make the binary executable
if err := os.Chmod(file, 0755); err != nil {
LocalICURL = strings.ReplaceAll(LocalICURL, DefaultVersion, version)
}

dir := path.Join(currentDir, "bin")
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
return err
}

filePath := path.Join(dir, file)

if err := downloadWithProgress(filePath, LocalICURL); err != nil {
return err
}

fmt.Printf("Downloaded Local Interchain binary to %s\n", path.Join(currentDir, file))
if err := os.Chmod(file, 0755); err != nil {
return err
}

fmt.Printf("✅ Local Interchain Downloaded to %s\n", filePath)
return nil
}

Expand All @@ -69,10 +153,16 @@ func downloadWithProgress(destinationPath, downloadUrl string) error {
return err
}

bar := progressbar.DefaultBytes(
bar := progressbar.NewOptions64(
resp.ContentLength,
"Downloading local-ic",
progressbar.OptionSetDescription("⏳ Downloading Local-Interchain..."),
progressbar.OptionSetWidth(50),
progressbar.OptionThrottle(0*time.Millisecond),
progressbar.OptionOnCompletion(func() {
fmt.Fprint(os.Stderr, "\n")
}),
)

io.Copy(io.MultiWriter(f, bar), resp.Body)
return nil
}
2 changes: 1 addition & 1 deletion cmd/spawn/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const (

func main() {
rootCmd.AddCommand(newChain)
rootCmd.AddCommand(DownloadLocalIC)
rootCmd.AddCommand(LocalICCmd)
rootCmd.AddCommand(BuildAppImage)

if err := rootCmd.Execute(); err != nil {
Expand Down
76 changes: 68 additions & 8 deletions cmd/spawn/new-chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"path"
"strings"

"github.com/cosmos/btcutil/bech32"
"github.com/spf13/cobra"
"github.com/strangelove-ventures/simapp"
)
Expand All @@ -33,10 +34,11 @@ const (
FlagTokenDenom = "denom"

FlagDisabled = "disable"
FlagNoGit = "no-git"
)

var (
IgnoredFiles = []string{"generate.sh", "embed.go"}
IgnoredFiles = []string{"embed.go", "heighliner/"}
SupportedFeatures = []string{"tokenfactory", "poa", "globalfee", "wasm", "ibc", "nft", "group", "circuit"}
)

Expand All @@ -46,6 +48,7 @@ func init() {
newChain.Flags().Bool(FlagDebugging, false, "enable debugging")
newChain.Flags().StringSlice(FlagDisabled, []string{}, "disable features: "+strings.Join(SupportedFeatures, ", "))
newChain.Flags().String(FlagTokenDenom, "stake", "token denom")
newChain.Flags().Bool(FlagNoGit, false, "git init base")
}

// TODO: reduce required inputs here. (or make them flags with defaults?)
Expand All @@ -69,6 +72,8 @@ var newChain = &cobra.Command{

disabled, _ := cmd.Flags().GetStringSlice(FlagDisabled)

ignoreGitInit, _ := cmd.Flags().GetBool(FlagNoGit)

cfg := SpawnNewConfig{
ProjectName: projName,
Bech32Prefix: walletPrefix,
Expand All @@ -84,12 +89,38 @@ var newChain = &cobra.Command{

NewChain(cfg)

// Create the base git repo
if !ignoreGitInit {

// if git already exists, don't init
if err := execCommand("git", "init", projName, "--quiet"); err != nil {
fmt.Println("Error initializing git:", err)
}
if err := os.Chdir(projName); err != nil {
fmt.Println("Error changing to project directory:", err)
}
if err := execCommand("git", "add", "."); err != nil {
fmt.Println("Error adding files to git:", err)
}
if err := execCommand("git", "commit", "-m", "initial commit", "--quiet"); err != nil {
fmt.Println("Error committing initial files:", err)
}
}

// Announce how to use it
fmt.Printf("\n\n🎉 New blockchain '%s' generated!\n", projName)
fmt.Println("🏅Getting started:")
fmt.Println(" - $ cd " + projName)
fmt.Println(" - $ make testnet # build & start the testnet")
fmt.Printf(" - $ make install # build the %s binary\n", binName)
fmt.Println(" - $ make local-image # build docker image")
},
}

func NewChain(cfg SpawnNewConfig) {
NewDirName := cfg.ProjectName
bech32Prefix := cfg.Bech32Prefix
projName := cfg.ProjectName
appName := cfg.AppName
appDirName := cfg.AppDirName
binaryName := cfg.BinaryName
Expand Down Expand Up @@ -123,7 +154,7 @@ func NewChain(cfg SpawnNewConfig) {
}

for _, ignoreFile := range IgnoredFiles {
if strings.HasSuffix(newPath, ignoreFile) {
if strings.HasSuffix(newPath, ignoreFile) || strings.HasPrefix(newPath, ignoreFile) {
if Debugging {
fmt.Println("ignoring", newPath)
}
Expand All @@ -150,6 +181,10 @@ func NewChain(cfg SpawnNewConfig) {
fc = strings.ReplaceAll(fc, "export DENOM=${DENOM:-token}", fmt.Sprintf("export DENOM=${DENOM:-%s}", cfg.TokenDenom))
}

if relPath == "Dockerfile" {
fc = strings.ReplaceAll(fc, "wasmd", binaryName)
}

// TODO: regex would be nicer for replacing incase it changes up stream. may never though. Also limit to specific files?
fc = strings.ReplaceAll(fc, ".wasmd", appDirName)
fc = strings.ReplaceAll(fc, `const appName = "WasmApp"`, fmt.Sprintf(`const appName = "%s"`, appName))
Expand All @@ -160,16 +195,41 @@ func NewChain(cfg SpawnNewConfig) {
fc = strings.ReplaceAll(fc, "https://github.com/CosmWasm/wasmd.git", fmt.Sprintf("https://%s.git", goModName))
fc = strings.ReplaceAll(fc, "version.Name=wasm", fmt.Sprintf("version.Name=%s", appName)) // ldflags
fc = strings.ReplaceAll(fc, "version.AppName=wasmd", fmt.Sprintf("version.AppName=%s", binaryName))
fc = strings.ReplaceAll(fc, "github.com/CosmWasm/wasmd/app.Bech32Prefix=wasm", fmt.Sprintf("%s/app.Bech32Prefix=%s", goModName, bech32Prefix))
fc = strings.ReplaceAll(fc, "cmd/wasmd", fmt.Sprintf("cmd/%s", binaryName))
fc = strings.ReplaceAll(fc, "build/wasmd", fmt.Sprintf("build/%s", binaryName))

// heighliner
if strings.HasSuffix(relPath, "chains.yaml") {
fc = strings.ReplaceAll(fc, "MyAppName", appName)
fc = strings.ReplaceAll(fc, "MyAppBinary", binaryName)
// heighliner (not working atm)
fc = strings.ReplaceAll(fc, "docker build . -t wasmd:local", fmt.Sprintf(`docker build . -t %s:local`, strings.ToLower(projName)))
// fc = strings.ReplaceAll(fc, "heighliner build -c wasmd --local --dockerfile=cosmos -f chains.yaml", fmt.Sprintf(`heighliner build -c %s --local --dockerfile=cosmos -f chains.yaml`, strings.ToLower(appName)))
// if strings.HasSuffix(relPath, "chains.yaml") {
// fc = strings.ReplaceAll(fc, "myappname", strings.ToLower(appName))
// fc = strings.ReplaceAll(fc, "/go/bin/wasmd", fmt.Sprintf("/go/bin/%s", binaryName))
// }

// local-interchain config
if strings.HasSuffix(relPath, "testnet.json") {
fc = strings.ReplaceAll(fc, `"repository": "wasmd"`, fmt.Sprintf(`"repository": "%s"`, strings.ToLower(projName)))
fc = strings.ReplaceAll(fc, `"bech32_prefix": "wasm"`, fmt.Sprintf(`"bech32_prefix": "%s"`, bech32Prefix))
fc = strings.ReplaceAll(fc, "appName", projName)
fc = strings.ReplaceAll(fc, "mydenom", cfg.TokenDenom)
fc = strings.ReplaceAll(fc, "wasmd", binaryName)

// making dynamic would be nice
for _, addr := range []string{"wasm1hj5fveer5cjtn4wd6wstzugjfdxzl0xpvsr89g", "wasm1efd63aw40lxf3n4mhf7dzhjkr453axursysrvp"} {
// bech32 convert to the new prefix
_, bz, err := bech32.Decode(addr, 100)
if err != nil {
panic(err)
}

newAddr, err := bech32.Encode(bech32Prefix, bz)
if err != nil {
panic(err)
}

fc = strings.ReplaceAll(fc, addr, newAddr)
}
}
fc = strings.ReplaceAll(fc, "heighliner build -c juno --local -f ./chains.yaml", fmt.Sprintf(`heighliner build -c %s --local -f ./chains.yaml`, strings.ToLower(appName)))

// if the relPath is cmd/wasmd, replace it to be cmd/binaryName
if strings.HasPrefix(relPath, "cmd/wasmd") {
Expand Down
Loading

0 comments on commit ea23fca

Please sign in to comment.