From 3e62d6d0b3b78723c695a220b138ab605524f3e5 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Tue, 16 Sep 2025 21:59:13 +0200 Subject: [PATCH 01/11] feat(cosmosgen): fetch fallback buf token --- ignite/pkg/cosmosgen/generate_typescript.go | 46 ++++- .../pkg/cosmosgen/generate_typescript_test.go | 178 ++++++++++++++++++ 2 files changed, 223 insertions(+), 1 deletion(-) diff --git a/ignite/pkg/cosmosgen/generate_typescript.go b/ignite/pkg/cosmosgen/generate_typescript.go index c6919d805a..f731a09027 100644 --- a/ignite/pkg/cosmosgen/generate_typescript.go +++ b/ignite/pkg/cosmosgen/generate_typescript.go @@ -2,12 +2,16 @@ package cosmosgen import ( "context" + "encoding/json" + "fmt" "log" + "net/http" "os" "os/exec" "path/filepath" "sort" "strings" + "time" "golang.org/x/sync/errgroup" @@ -19,11 +23,15 @@ import ( ) var ( + bufTokenEnvName = "BUF_TOKEN" + dirchangeCacheNamespace = "generate.typescript.dirchange" protocGenTSProtoBin = "protoc-gen-ts_proto" msgBufAuth = "Note: Buf is limits remote plugin requests from unauthenticated users on 'buf.build'. Intensively using this function will get you rate limited. Authenticate with 'buf registry login' to avoid this (https://buf.build/docs/generate/auth-required)." + + bufTokenEndpoint = "https://api.ignite.com/v1/buf/token" ) const localTSProtoTmpl = `version: v1 @@ -54,7 +62,15 @@ func newTSGenerator(g *generator) *tsGenerator { } if !tsg.isLocalProto { - log.Printf("No '%s' binary found in PATH, using remote buf plugin for Typescript generation. %s\n", protocGenTSProtoBin, msgBufAuth) + if os.Getenv(bufTokenEnvName) == "" { + // fetch ignite buf.build token + token, err := fetchBufToken() + if err != nil { + log.Printf("No '%s' binary found in PATH, using remote buf plugin for Typescript generation. %s\n", protocGenTSProtoBin, msgBufAuth) + } else { + os.Setenv(bufTokenEnvName, token) + } + } } return tsg @@ -250,3 +266,31 @@ func (g *tsGenerator) generateRootTemplates(p generatePayload) error { return templateTSClientRoot.Write(outDir, "", p) } + +// fetchBufToken fetches the buf token from the ignite API +func fetchBufToken() (string, error) { + client := &http.Client{ + Timeout: 10 * time.Second, + } + + resp, err := client.Get(bufTokenEndpoint) + if err != nil { + return "", err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return "", fmt.Errorf("HTTP request failed with status code: %d", resp.StatusCode) + } + + type tokenResponse struct { + Token string `json:"token"` + } + var tokenResp tokenResponse + + if err := json.NewDecoder(resp.Body).Decode(&tokenResp); err != nil { + return "", err + } + + return tokenResp.Token, nil +} diff --git a/ignite/pkg/cosmosgen/generate_typescript_test.go b/ignite/pkg/cosmosgen/generate_typescript_test.go index ad806ce201..307eecdfb9 100644 --- a/ignite/pkg/cosmosgen/generate_typescript_test.go +++ b/ignite/pkg/cosmosgen/generate_typescript_test.go @@ -2,6 +2,8 @@ package cosmosgen import ( "fmt" + "net/http" + "net/http/httptest" "os" "path/filepath" "strings" @@ -75,3 +77,179 @@ func TestGenerateTypeScript(t *testing.T) { return nil }) } + +func TestFetchBufToken(t *testing.T) { + tests := []struct { + name string + serverResponse string + statusCode int + expectedToken string + expectError bool + }{ + { + name: "successful token fetch", + serverResponse: `{"token":"test_token_123"}`, + statusCode: http.StatusOK, + expectedToken: "test_token_123", + expectError: false, + }, + { + name: "server error", + serverResponse: `{"error":"internal server error"}`, + statusCode: http.StatusInternalServerError, + expectedToken: "", + expectError: true, + }, + { + name: "invalid json response", + serverResponse: `invalid json`, + statusCode: http.StatusOK, + expectedToken: "", + expectError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Create mock server + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(tt.statusCode) + w.Write([]byte(tt.serverResponse)) + })) + defer server.Close() + + // Temporarily override the endpoint + originalEndpoint := bufTokenEndpoint + bufTokenEndpoint = server.URL + defer func() { + bufTokenEndpoint = originalEndpoint + }() + + token, err := fetchBufToken() + + if tt.expectError { + require.Error(t, err) + require.Empty(t, token) + } else { + require.NoError(t, err) + require.Equal(t, tt.expectedToken, token) + } + }) + } +} + +func TestNewTSGeneratorBufTokenLogic(t *testing.T) { + tests := []struct { + name string + isLocalProto bool + existingEnvToken string + mockServerResponse string + mockStatusCode int + expectEnvSet bool + expectLogMessage bool + }{ + { + name: "local proto available - no token fetch", + isLocalProto: true, + existingEnvToken: "", + mockServerResponse: "", + mockStatusCode: 0, + expectEnvSet: false, + expectLogMessage: false, + }, + { + name: "env token already set - no fetch", + isLocalProto: false, + existingEnvToken: "existing_token", + mockServerResponse: "", + mockStatusCode: 0, + expectEnvSet: false, + expectLogMessage: false, + }, + { + name: "successful token fetch", + isLocalProto: false, + existingEnvToken: "", + mockServerResponse: `{"token":"fetched_token"}`, + mockStatusCode: http.StatusOK, + expectEnvSet: true, + expectLogMessage: false, + }, + { + name: "failed token fetch - log message", + isLocalProto: false, + existingEnvToken: "", + mockServerResponse: `{"error":"server error"}`, + mockStatusCode: http.StatusInternalServerError, + expectEnvSet: false, + expectLogMessage: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Save original environment + originalToken := os.Getenv(bufTokenEnvName) + originalEndpoint := bufTokenEndpoint + defer func() { + if originalToken != "" { + os.Setenv(bufTokenEnvName, originalToken) + } else { + os.Unsetenv(bufTokenEnvName) + } + bufTokenEndpoint = originalEndpoint + }() + + // Set up test environment + if tt.existingEnvToken != "" { + os.Setenv(bufTokenEnvName, tt.existingEnvToken) + } else { + os.Unsetenv(bufTokenEnvName) + } + + // Set up mock server if needed + if !tt.isLocalProto && tt.existingEnvToken == "" { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(tt.mockStatusCode) + w.Write([]byte(tt.mockServerResponse)) + })) + defer server.Close() + bufTokenEndpoint = server.URL + } + + // Create mock generator + g := &generator{} + + // Create TSGenerator with mocked isLocalProto + tsg := &tsGenerator{ + g: g, + isLocalProto: tt.isLocalProto, + } + + // Simulate the logic from newTSGenerator + if !tsg.isLocalProto { + if os.Getenv(bufTokenEnvName) == "" { + token, err := fetchBufToken() + if err != nil { + // This would normally log the message + if tt.expectLogMessage { + require.Error(t, err) + } + } else { + os.Setenv(bufTokenEnvName, token) + } + } + } + + // Verify expectations + if tt.expectEnvSet { + token := os.Getenv(bufTokenEnvName) + require.NotEmpty(t, token) + require.Equal(t, "fetched_token", token) + } else if tt.existingEnvToken != "" { + token := os.Getenv(bufTokenEnvName) + require.Equal(t, tt.existingEnvToken, token) + } + }) + } +} From 55c6a7b15ccfe82820b4f56967292370cebd082e Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Wed, 17 Sep 2025 10:36:39 +0200 Subject: [PATCH 02/11] package refactor --- ignite/cmd/bubblemodel/chain_serve.go | 2 +- ignite/cmd/cmd.go | 2 +- ignite/internal/analytics/analytics.go | 2 +- .../announcements/announcement.go | 15 +- .../announcements/announcement_test.go | 8 +- ignite/internal/buf/buf.go | 38 ++++ ignite/internal/buf/buf_test.go | 69 +++++++ ignite/{pkg => internal}/sentry/sentry.go | 0 ignite/pkg/cosmosgen/generate_typescript.go | 47 +---- .../pkg/cosmosgen/generate_typescript_test.go | 178 ------------------ .../testchain/proto/buf.gen.swagger.yaml | 2 +- ignite/pkg/errors/xerrors.go | 6 +- .../app/files/{{protoDir}}/buf.gen.gogo.yaml | 2 +- .../app/files/{{protoDir}}/buf.gen.sta.yaml | 2 +- .../files/{{protoDir}}/buf.gen.swagger.yaml | 2 +- .../app/files/{{protoDir}}/buf.gen.ts.yaml | 2 +- 16 files changed, 137 insertions(+), 240 deletions(-) rename ignite/{pkg => internal}/announcements/announcement.go (78%) rename ignite/{pkg => internal}/announcements/announcement_test.go (87%) create mode 100644 ignite/internal/buf/buf.go create mode 100644 ignite/internal/buf/buf_test.go rename ignite/{pkg => internal}/sentry/sentry.go (100%) diff --git a/ignite/cmd/bubblemodel/chain_serve.go b/ignite/cmd/bubblemodel/chain_serve.go index 08cfa298f5..088c81c091 100644 --- a/ignite/cmd/bubblemodel/chain_serve.go +++ b/ignite/cmd/bubblemodel/chain_serve.go @@ -7,7 +7,7 @@ import ( tea "github.com/charmbracelet/bubbletea" - "github.com/ignite/cli/v29/ignite/pkg/announcements" + "github.com/ignite/cli/v29/ignite/internal/announcements" "github.com/ignite/cli/v29/ignite/pkg/cliui/colors" "github.com/ignite/cli/v29/ignite/pkg/cliui/icons" cliuimodel "github.com/ignite/cli/v29/ignite/pkg/cliui/model" diff --git a/ignite/cmd/cmd.go b/ignite/cmd/cmd.go index e5b937be35..19979a3cfd 100644 --- a/ignite/cmd/cmd.go +++ b/ignite/cmd/cmd.go @@ -14,7 +14,7 @@ import ( "github.com/ignite/cli/v29/ignite/config" chainconfig "github.com/ignite/cli/v29/ignite/config/chain" - "github.com/ignite/cli/v29/ignite/pkg/announcements" + "github.com/ignite/cli/v29/ignite/internal/announcements" "github.com/ignite/cli/v29/ignite/pkg/cache" "github.com/ignite/cli/v29/ignite/pkg/cliui" uilog "github.com/ignite/cli/v29/ignite/pkg/cliui/log" diff --git a/ignite/internal/analytics/analytics.go b/ignite/internal/analytics/analytics.go index 5c55290d37..5a83512003 100644 --- a/ignite/internal/analytics/analytics.go +++ b/ignite/internal/analytics/analytics.go @@ -12,11 +12,11 @@ import ( "github.com/spf13/cobra" "github.com/ignite/cli/v29/ignite/config" + "github.com/ignite/cli/v29/ignite/internal/sentry" "github.com/ignite/cli/v29/ignite/pkg/cliui" "github.com/ignite/cli/v29/ignite/pkg/errors" "github.com/ignite/cli/v29/ignite/pkg/matomo" "github.com/ignite/cli/v29/ignite/pkg/randstr" - "github.com/ignite/cli/v29/ignite/pkg/sentry" "github.com/ignite/cli/v29/ignite/version" ) diff --git a/ignite/pkg/announcements/announcement.go b/ignite/internal/announcements/announcement.go similarity index 78% rename from ignite/pkg/announcements/announcement.go rename to ignite/internal/announcements/announcement.go index 7b1947f54a..0c3c5058ad 100644 --- a/ignite/pkg/announcements/announcement.go +++ b/ignite/internal/announcements/announcement.go @@ -11,14 +11,10 @@ import ( ) var ( - SurveyLink = "https://bit.ly/3WZS2uS" - APIURL = "http://announcements.ignite.com/v1/announcements" + SurveyLink = "https://bit.ly/3WZS2uS" + AnnouncementURL = "https://api.ignite.com/v1/announcements" ) -type api struct { - Announcements []announcement `json:"announcements"` -} - type announcement struct { ID string `json:"id"` Text string `json:"text"` @@ -28,13 +24,16 @@ type announcement struct { // Fetch fetches the latest announcements from the API. func Fetch() string { - resp, err := http.Get(APIURL) //nolint:gosec + resp, err := http.Get(AnnouncementURL) //nolint:gosec if err != nil || resp.StatusCode != 200 { return fallbackData() } defer resp.Body.Close() - var data api + type response struct { + Announcements []announcement `json:"announcements"` + } + var data response if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { return fallbackData() } diff --git a/ignite/pkg/announcements/announcement_test.go b/ignite/internal/announcements/announcement_test.go similarity index 87% rename from ignite/pkg/announcements/announcement_test.go rename to ignite/internal/announcements/announcement_test.go index eb1796c881..23a8e5b3c9 100644 --- a/ignite/pkg/announcements/announcement_test.go +++ b/ignite/internal/announcements/announcement_test.go @@ -6,7 +6,7 @@ import ( "net/http/httptest" "testing" - "github.com/ignite/cli/v29/ignite/pkg/announcements" + "github.com/ignite/cli/v29/ignite/internal/announcements" ) func TestFetchAnnouncements(t *testing.T) { @@ -52,9 +52,9 @@ func TestFetchAnnouncements(t *testing.T) { })) defer server.Close() - originalAPI := announcements.APIURL - announcements.APIURL = server.URL - defer func() { announcements.APIURL = originalAPI }() + originalAPI := announcements.AnnouncementURL + announcements.AnnouncementURL = server.URL + defer func() { announcements.AnnouncementURL = originalAPI }() result := announcements.Fetch() if result != tt.expected { diff --git a/ignite/internal/buf/buf.go b/ignite/internal/buf/buf.go new file mode 100644 index 0000000000..fbbe73e5e4 --- /dev/null +++ b/ignite/internal/buf/buf.go @@ -0,0 +1,38 @@ +package buf + +import ( + "encoding/json" + "fmt" + "net/http" + "time" +) + +var BufTokenURL = "https://api.ignite.com/v1/buf/token" + +// FetchToken fetches the buf token from the Ignite API. +func FetchToken() (string, error) { + client := &http.Client{ + Timeout: 5 * time.Second, + } + + resp, err := client.Get(BufTokenURL) + if err != nil { + return "", err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return "", fmt.Errorf("HTTP request failed with status code: %d", resp.StatusCode) + } + + type tokenResponse struct { + Token string `json:"token"` + } + var tokenResp tokenResponse + + if err := json.NewDecoder(resp.Body).Decode(&tokenResp); err != nil { + return "", err + } + + return tokenResp.Token, nil +} diff --git a/ignite/internal/buf/buf_test.go b/ignite/internal/buf/buf_test.go new file mode 100644 index 0000000000..a2f167cd6e --- /dev/null +++ b/ignite/internal/buf/buf_test.go @@ -0,0 +1,69 @@ +package buf_test + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/ignite/cli/v29/ignite/internal/buf" + "github.com/stretchr/testify/require" +) + +func TestFetchToken(t *testing.T) { + tests := []struct { + name string + serverResponse string + statusCode int + expectedToken string + expectError bool + }{ + { + name: "successful token fetch", + serverResponse: `{"token":"test_token_123"}`, + statusCode: http.StatusOK, + expectedToken: "test_token_123", + expectError: false, + }, + { + name: "server error", + serverResponse: `{"error":"internal server error"}`, + statusCode: http.StatusInternalServerError, + expectedToken: "", + expectError: true, + }, + { + name: "invalid json response", + serverResponse: `invalid json`, + statusCode: http.StatusOK, + expectedToken: "", + expectError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Create mock server + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(tt.statusCode) + w.Write([]byte(tt.serverResponse)) + })) + defer server.Close() + + // Temporarily override the endpoint + originalEndpoint := buf.BufTokenURL + buf.BufTokenURL = server.URL + defer func() { + buf.BufTokenURL = originalEndpoint + }() + + token, err := buf.FetchToken() + if tt.expectError { + require.Error(t, err) + require.Empty(t, token) + } else { + require.NoError(t, err) + require.Equal(t, tt.expectedToken, token) + } + }) + } +} diff --git a/ignite/pkg/sentry/sentry.go b/ignite/internal/sentry/sentry.go similarity index 100% rename from ignite/pkg/sentry/sentry.go rename to ignite/internal/sentry/sentry.go diff --git a/ignite/pkg/cosmosgen/generate_typescript.go b/ignite/pkg/cosmosgen/generate_typescript.go index f731a09027..0b5706ee49 100644 --- a/ignite/pkg/cosmosgen/generate_typescript.go +++ b/ignite/pkg/cosmosgen/generate_typescript.go @@ -2,19 +2,16 @@ package cosmosgen import ( "context" - "encoding/json" - "fmt" "log" - "net/http" "os" "os/exec" "path/filepath" "sort" "strings" - "time" "golang.org/x/sync/errgroup" + "github.com/ignite/cli/v29/ignite/internal/buf" "github.com/ignite/cli/v29/ignite/pkg/cache" "github.com/ignite/cli/v29/ignite/pkg/cosmosanalysis/module" "github.com/ignite/cli/v29/ignite/pkg/cosmosbuf" @@ -30,8 +27,6 @@ var ( protocGenTSProtoBin = "protoc-gen-ts_proto" msgBufAuth = "Note: Buf is limits remote plugin requests from unauthenticated users on 'buf.build'. Intensively using this function will get you rate limited. Authenticate with 'buf registry login' to avoid this (https://buf.build/docs/generate/auth-required)." - - bufTokenEndpoint = "https://api.ignite.com/v1/buf/token" ) const localTSProtoTmpl = `version: v1 @@ -39,9 +34,12 @@ plugins: - plugin: ts_proto out: . opt: - - "esModuleInterop=true" - - "forceLong=long" - - "useOptionals=true" + - logtostderr=true + - allow_merge=true + - json_names_for_fields=false + - ts_proto_opt=snakeToCamel=true + - ts_proto_opt=esModuleInterop=true + - ts_proto_out=. ` type tsGenerator struct { @@ -63,8 +61,7 @@ func newTSGenerator(g *generator) *tsGenerator { if !tsg.isLocalProto { if os.Getenv(bufTokenEnvName) == "" { - // fetch ignite buf.build token - token, err := fetchBufToken() + token, err := buf.FetchToken() if err != nil { log.Printf("No '%s' binary found in PATH, using remote buf plugin for Typescript generation. %s\n", protocGenTSProtoBin, msgBufAuth) } else { @@ -266,31 +263,3 @@ func (g *tsGenerator) generateRootTemplates(p generatePayload) error { return templateTSClientRoot.Write(outDir, "", p) } - -// fetchBufToken fetches the buf token from the ignite API -func fetchBufToken() (string, error) { - client := &http.Client{ - Timeout: 10 * time.Second, - } - - resp, err := client.Get(bufTokenEndpoint) - if err != nil { - return "", err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return "", fmt.Errorf("HTTP request failed with status code: %d", resp.StatusCode) - } - - type tokenResponse struct { - Token string `json:"token"` - } - var tokenResp tokenResponse - - if err := json.NewDecoder(resp.Body).Decode(&tokenResp); err != nil { - return "", err - } - - return tokenResp.Token, nil -} diff --git a/ignite/pkg/cosmosgen/generate_typescript_test.go b/ignite/pkg/cosmosgen/generate_typescript_test.go index 307eecdfb9..ad806ce201 100644 --- a/ignite/pkg/cosmosgen/generate_typescript_test.go +++ b/ignite/pkg/cosmosgen/generate_typescript_test.go @@ -2,8 +2,6 @@ package cosmosgen import ( "fmt" - "net/http" - "net/http/httptest" "os" "path/filepath" "strings" @@ -77,179 +75,3 @@ func TestGenerateTypeScript(t *testing.T) { return nil }) } - -func TestFetchBufToken(t *testing.T) { - tests := []struct { - name string - serverResponse string - statusCode int - expectedToken string - expectError bool - }{ - { - name: "successful token fetch", - serverResponse: `{"token":"test_token_123"}`, - statusCode: http.StatusOK, - expectedToken: "test_token_123", - expectError: false, - }, - { - name: "server error", - serverResponse: `{"error":"internal server error"}`, - statusCode: http.StatusInternalServerError, - expectedToken: "", - expectError: true, - }, - { - name: "invalid json response", - serverResponse: `invalid json`, - statusCode: http.StatusOK, - expectedToken: "", - expectError: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Create mock server - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(tt.statusCode) - w.Write([]byte(tt.serverResponse)) - })) - defer server.Close() - - // Temporarily override the endpoint - originalEndpoint := bufTokenEndpoint - bufTokenEndpoint = server.URL - defer func() { - bufTokenEndpoint = originalEndpoint - }() - - token, err := fetchBufToken() - - if tt.expectError { - require.Error(t, err) - require.Empty(t, token) - } else { - require.NoError(t, err) - require.Equal(t, tt.expectedToken, token) - } - }) - } -} - -func TestNewTSGeneratorBufTokenLogic(t *testing.T) { - tests := []struct { - name string - isLocalProto bool - existingEnvToken string - mockServerResponse string - mockStatusCode int - expectEnvSet bool - expectLogMessage bool - }{ - { - name: "local proto available - no token fetch", - isLocalProto: true, - existingEnvToken: "", - mockServerResponse: "", - mockStatusCode: 0, - expectEnvSet: false, - expectLogMessage: false, - }, - { - name: "env token already set - no fetch", - isLocalProto: false, - existingEnvToken: "existing_token", - mockServerResponse: "", - mockStatusCode: 0, - expectEnvSet: false, - expectLogMessage: false, - }, - { - name: "successful token fetch", - isLocalProto: false, - existingEnvToken: "", - mockServerResponse: `{"token":"fetched_token"}`, - mockStatusCode: http.StatusOK, - expectEnvSet: true, - expectLogMessage: false, - }, - { - name: "failed token fetch - log message", - isLocalProto: false, - existingEnvToken: "", - mockServerResponse: `{"error":"server error"}`, - mockStatusCode: http.StatusInternalServerError, - expectEnvSet: false, - expectLogMessage: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Save original environment - originalToken := os.Getenv(bufTokenEnvName) - originalEndpoint := bufTokenEndpoint - defer func() { - if originalToken != "" { - os.Setenv(bufTokenEnvName, originalToken) - } else { - os.Unsetenv(bufTokenEnvName) - } - bufTokenEndpoint = originalEndpoint - }() - - // Set up test environment - if tt.existingEnvToken != "" { - os.Setenv(bufTokenEnvName, tt.existingEnvToken) - } else { - os.Unsetenv(bufTokenEnvName) - } - - // Set up mock server if needed - if !tt.isLocalProto && tt.existingEnvToken == "" { - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(tt.mockStatusCode) - w.Write([]byte(tt.mockServerResponse)) - })) - defer server.Close() - bufTokenEndpoint = server.URL - } - - // Create mock generator - g := &generator{} - - // Create TSGenerator with mocked isLocalProto - tsg := &tsGenerator{ - g: g, - isLocalProto: tt.isLocalProto, - } - - // Simulate the logic from newTSGenerator - if !tsg.isLocalProto { - if os.Getenv(bufTokenEnvName) == "" { - token, err := fetchBufToken() - if err != nil { - // This would normally log the message - if tt.expectLogMessage { - require.Error(t, err) - } - } else { - os.Setenv(bufTokenEnvName, token) - } - } - } - - // Verify expectations - if tt.expectEnvSet { - token := os.Getenv(bufTokenEnvName) - require.NotEmpty(t, token) - require.Equal(t, "fetched_token", token) - } else if tt.existingEnvToken != "" { - token := os.Getenv(bufTokenEnvName) - require.Equal(t, tt.existingEnvToken, token) - } - }) - } -} diff --git a/ignite/pkg/cosmosgen/testdata/testchain/proto/buf.gen.swagger.yaml b/ignite/pkg/cosmosgen/testdata/testchain/proto/buf.gen.swagger.yaml index 0061ef290e..d81685c33d 100644 --- a/ignite/pkg/cosmosgen/testdata/testchain/proto/buf.gen.swagger.yaml +++ b/ignite/pkg/cosmosgen/testdata/testchain/proto/buf.gen.swagger.yaml @@ -1,4 +1,4 @@ -# This file is auto-generated from Ignite. You can edit +# This file is auto-generated by Ignite. You can edit # the file content but do not change the file name or path. # # buf.gen.swagger.yaml diff --git a/ignite/pkg/errors/xerrors.go b/ignite/pkg/errors/xerrors.go index 66ba2c074e..587714b61e 100644 --- a/ignite/pkg/errors/xerrors.go +++ b/ignite/pkg/errors/xerrors.go @@ -26,7 +26,7 @@ func New(msg string) error { } // Errorf aliases Newf(). -func Errorf(format string, args ...interface{}) error { +func Errorf(format string, args ...any) error { err := errors.Errorf(format, args...) sentry.CaptureException(err) return err @@ -49,7 +49,7 @@ func Wrap(err error, msg string) error { // Wrapf wraps an error with a formatted message prefix. A stack // trace is also retained. If the format is empty, no prefix is added, // but the extra arguments are still processed for reportable strings. -func Wrapf(err error, format string, args ...interface{}) error { +func Wrapf(err error, format string, args ...any) error { errWrap := errors.Wrapf(err, format, args...) sentry.CaptureException(errWrap) return errWrap @@ -73,4 +73,4 @@ func Is(err, reference error) bool { return errors.Is(err, reference) } // matches a type if it is assignable to the target type, or if it has a method // As(interface{}) bool such that As(target) returns true. As will panic if target // is not a non-nil pointer to a type which implements error or is of interface type. -func As(err error, target interface{}) bool { return errors.As(err, target) } +func As(err error, target any) bool { return errors.As(err, target) } diff --git a/ignite/templates/app/files/{{protoDir}}/buf.gen.gogo.yaml b/ignite/templates/app/files/{{protoDir}}/buf.gen.gogo.yaml index 1f6ecfe5e0..a3b72cc944 100644 --- a/ignite/templates/app/files/{{protoDir}}/buf.gen.gogo.yaml +++ b/ignite/templates/app/files/{{protoDir}}/buf.gen.gogo.yaml @@ -1,4 +1,4 @@ -# This file is auto-generated from Ignite. You can edit +# This file is auto-generated by Ignite. You can edit # the file content but do not change the file name or path. # # buf.gen.gogo.yaml diff --git a/ignite/templates/app/files/{{protoDir}}/buf.gen.sta.yaml b/ignite/templates/app/files/{{protoDir}}/buf.gen.sta.yaml index 215d950d94..7e69ed5d2e 100644 --- a/ignite/templates/app/files/{{protoDir}}/buf.gen.sta.yaml +++ b/ignite/templates/app/files/{{protoDir}}/buf.gen.sta.yaml @@ -1,4 +1,4 @@ -# This file is auto-generated from Ignite. You can edit +# This file is auto-generated by Ignite. You can edit # the file content but do not change the file name or path. # # buf.gen.sta.yaml diff --git a/ignite/templates/app/files/{{protoDir}}/buf.gen.swagger.yaml b/ignite/templates/app/files/{{protoDir}}/buf.gen.swagger.yaml index 0061ef290e..d81685c33d 100644 --- a/ignite/templates/app/files/{{protoDir}}/buf.gen.swagger.yaml +++ b/ignite/templates/app/files/{{protoDir}}/buf.gen.swagger.yaml @@ -1,4 +1,4 @@ -# This file is auto-generated from Ignite. You can edit +# This file is auto-generated by Ignite. You can edit # the file content but do not change the file name or path. # # buf.gen.swagger.yaml diff --git a/ignite/templates/app/files/{{protoDir}}/buf.gen.ts.yaml b/ignite/templates/app/files/{{protoDir}}/buf.gen.ts.yaml index bc2f01b93c..9bd0f601e7 100644 --- a/ignite/templates/app/files/{{protoDir}}/buf.gen.ts.yaml +++ b/ignite/templates/app/files/{{protoDir}}/buf.gen.ts.yaml @@ -1,4 +1,4 @@ -# This file is auto-generated from Ignite. You can edit +# This file is auto-generated by Ignite. You can edit # the file content but do not change the file name or path. # # buf.gen.ts.yaml From 293a213936bd362a89f1142199e51e6538a8b12f Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Wed, 17 Sep 2025 18:01:12 +0200 Subject: [PATCH 03/11] fix `CaptureException called with nil error` --- ignite/pkg/errors/xerrors.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ignite/pkg/errors/xerrors.go b/ignite/pkg/errors/xerrors.go index 587714b61e..8261f898e6 100644 --- a/ignite/pkg/errors/xerrors.go +++ b/ignite/pkg/errors/xerrors.go @@ -42,7 +42,9 @@ func WithStack(err error) error { // Wrap wraps an error with a message prefix. A stack trace is retained. func Wrap(err error, msg string) error { errWrap := errors.Wrap(err, msg) - sentry.CaptureException(errWrap) + if err != nil { + sentry.CaptureException(errWrap) + } return errWrap } @@ -51,7 +53,9 @@ func Wrap(err error, msg string) error { // but the extra arguments are still processed for reportable strings. func Wrapf(err error, format string, args ...any) error { errWrap := errors.Wrapf(err, format, args...) - sentry.CaptureException(errWrap) + if err != nil { + sentry.CaptureException(errWrap) + } return errWrap } From d29589db294494394dd5b6c9ead38573434f8728 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Fri, 19 Sep 2025 23:30:27 +0200 Subject: [PATCH 04/11] cl --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index bab189ffec..3dd0947dc3 100644 --- a/changelog.md +++ b/changelog.md @@ -2,7 +2,7 @@ ## Unreleased -### Features +### Changes - [#4807](https://github.com/ignite/cli/pull/4807) Improve unconfigured path message when building a chain. From 4088343b988d2cae23cf799c00cbbb1a2e6dd33b Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Thu, 25 Sep 2025 20:52:58 +0200 Subject: [PATCH 05/11] updates --- changelog.md | 1 + ignite/internal/buf/buf.go | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index 3dd0947dc3..a66b2d4a4a 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,7 @@ ### Changes - [#4807](https://github.com/ignite/cli/pull/4807) Improve unconfigured path message when building a chain. +- [#4805](https://github.com/ignite/cli/pull/4805) Fetch fallback buf token. ## [`v29.4.0`](https://github.com/ignite/cli/releases/tag/v29.4.0) diff --git a/ignite/internal/buf/buf.go b/ignite/internal/buf/buf.go index fbbe73e5e4..268ee2af27 100644 --- a/ignite/internal/buf/buf.go +++ b/ignite/internal/buf/buf.go @@ -2,12 +2,13 @@ package buf import ( "encoding/json" - "fmt" "net/http" "time" + + "github.com/ignite/cli/v29/ignite/pkg/errors" ) -var BufTokenURL = "https://api.ignite.com/v1/buf/token" +var BufTokenURL = "https://buf.ignite.com" //nolint:gosec // URL is hardcoded and not user-provided // FetchToken fetches the buf token from the Ignite API. func FetchToken() (string, error) { @@ -22,7 +23,7 @@ func FetchToken() (string, error) { defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - return "", fmt.Errorf("HTTP request failed with status code: %d", resp.StatusCode) + return "", errors.Errorf("HTTP request failed with status code: %d", resp.StatusCode) } type tokenResponse struct { From e1f5cc8ca931eb39eafffa51afb1d7f423619012 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Thu, 25 Sep 2025 22:02:17 +0200 Subject: [PATCH 06/11] updates --- ignite/pkg/gocmd/gocmd.go | 8 -------- ignite/pkg/gocmd/gocmd_test.go | 9 --------- 2 files changed, 17 deletions(-) diff --git a/ignite/pkg/gocmd/gocmd.go b/ignite/pkg/gocmd/gocmd.go index c5216df876..db3aedb1e1 100644 --- a/ignite/pkg/gocmd/gocmd.go +++ b/ignite/pkg/gocmd/gocmd.go @@ -168,14 +168,6 @@ func Install(ctx context.Context, path string, pkgs []string, options ...exec.Op return exec.Exec(ctx, command, append(options, exec.StepOption(step.Workdir(path)))...) } -// IsInstallError returns true if err is interpreted as a go install error. -func IsInstallError(err error) bool { - if err == nil { - return false - } - return strings.Contains(err.Error(), "no required module provides package") -} - // Get runs go get pkgs on path with options. func Get(ctx context.Context, path string, pkgs []string, options ...exec.Option) error { command := []string{ diff --git a/ignite/pkg/gocmd/gocmd_test.go b/ignite/pkg/gocmd/gocmd_test.go index fc33beb654..bf3a3e69dd 100644 --- a/ignite/pkg/gocmd/gocmd_test.go +++ b/ignite/pkg/gocmd/gocmd_test.go @@ -7,18 +7,9 @@ import ( "github.com/stretchr/testify/assert" - "github.com/ignite/cli/v29/ignite/pkg/errors" "github.com/ignite/cli/v29/ignite/pkg/gocmd" ) -func TestIsInstallError(t *testing.T) { - assert.False(t, gocmd.IsInstallError(errors.New("oups"))) - - err := errors.New(`error while running command go install github.com/cosmos/gogoproto/protoc-gen-gocosmos google.golang.org/protobuf/cmd/protoc-gen-go github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2: no required module provides package github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2; to add it: - go get github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2`) - assert.True(t, gocmd.IsInstallError(err)) -} - func TestList(t *testing.T) { wd, err := os.Getwd() assert.NoError(t, err) From 4f90252b7ce5f92093f86c99894ff03786e37ced Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Thu, 25 Sep 2025 23:33:02 +0200 Subject: [PATCH 07/11] refactor: package cleanup --- ignite/internal/analytics/analytics.go | 11 +- .../matomo => internal/analytics}/matomo.go | 33 ++-- ignite/pkg/cosmoserror/error.go | 40 ----- ignite/pkg/cosmoserror/error_test.go | 88 ---------- ignite/pkg/cosmostestutil/sample/sample.go | 13 -- .../pkg/cosmostestutil/sample/sample_test.go | 16 -- ignite/pkg/ctxreader/ctxreader.go | 51 ------ ignite/pkg/ctxreader/ctxreader_test.go | 38 ---- ignite/pkg/numbers/numbers.go | 90 ---------- ignite/pkg/numbers/numbers_test.go | 73 -------- ignite/pkg/tendermintrpc/rpc.go | 163 ------------------ 11 files changed, 21 insertions(+), 595 deletions(-) rename ignite/{pkg/matomo => internal/analytics}/matomo.go (90%) delete mode 100644 ignite/pkg/cosmoserror/error.go delete mode 100644 ignite/pkg/cosmoserror/error_test.go delete mode 100644 ignite/pkg/cosmostestutil/sample/sample.go delete mode 100644 ignite/pkg/cosmostestutil/sample/sample_test.go delete mode 100644 ignite/pkg/ctxreader/ctxreader.go delete mode 100644 ignite/pkg/ctxreader/ctxreader_test.go delete mode 100644 ignite/pkg/numbers/numbers.go delete mode 100644 ignite/pkg/numbers/numbers_test.go delete mode 100644 ignite/pkg/tendermintrpc/rpc.go diff --git a/ignite/internal/analytics/analytics.go b/ignite/internal/analytics/analytics.go index 5a83512003..c29b4cc4e2 100644 --- a/ignite/internal/analytics/analytics.go +++ b/ignite/internal/analytics/analytics.go @@ -15,7 +15,6 @@ import ( "github.com/ignite/cli/v29/ignite/internal/sentry" "github.com/ignite/cli/v29/ignite/pkg/cliui" "github.com/ignite/cli/v29/ignite/pkg/errors" - "github.com/ignite/cli/v29/ignite/pkg/matomo" "github.com/ignite/cli/v29/ignite/pkg/randstr" "github.com/ignite/cli/v29/ignite/version" ) @@ -28,7 +27,7 @@ const ( igniteAnonIdentity = "anon_identity.json" ) -var matomoClient matomo.Client +var matomoClient MatomoClient // anonIdentity represents an analytics identity file. type anonIdentity struct { @@ -39,10 +38,10 @@ type anonIdentity struct { } func init() { - matomoClient = matomo.New( + matomoClient = NewMatomoClient( telemetryEndpoint, - matomo.WithIDSite(4), - matomo.WithSource("https://cli.ignite.com"), + WithIDSite(4), + WithSource("https://cli.ignite.com"), ) } @@ -73,7 +72,7 @@ func SendMetric(wg *sync.WaitGroup, cmd *cobra.Command) { } } - met := matomo.Metric{ + met := Metric{ Name: cmd.Name(), Cmd: path, ScaffoldType: scaffoldType, diff --git a/ignite/pkg/matomo/matomo.go b/ignite/internal/analytics/matomo.go similarity index 90% rename from ignite/pkg/matomo/matomo.go rename to ignite/internal/analytics/matomo.go index 66ce8b10d2..5007125757 100644 --- a/ignite/pkg/matomo/matomo.go +++ b/ignite/internal/analytics/matomo.go @@ -1,5 +1,4 @@ -// Package matomo is a client for Matomo to send data points for hint-type=event. -package matomo +package analytics import ( "crypto/rand" @@ -17,8 +16,8 @@ import ( ) type ( - // Client is an analytics client. - Client struct { + // MatomoClient is a matomo client. + MatomoClient struct { endpoint string idSite uint // Matomo ID Site. tokenAuth string // Matomo Token Auth. @@ -26,8 +25,8 @@ type ( httpClient http.Client } - // Params analytics metrics body. - Params struct { + // MatomoParams analytics metrics body. + MatomoParams struct { IDSite uint `url:"idsite"` Rec uint `url:"rec"` ActionName string `url:"action_name"` @@ -121,32 +120,32 @@ type ( ) // Option configures code generation. -type Option func(*Client) +type Option func(*MatomoClient) // WithIDSite adds an id site. func WithIDSite(idSite uint) Option { - return func(c *Client) { + return func(c *MatomoClient) { c.idSite = idSite } } // WithTokenAuth adds a matomo token authentication. func WithTokenAuth(tokenAuth string) Option { - return func(c *Client) { + return func(c *MatomoClient) { c.tokenAuth = tokenAuth } } // WithSource adds a matomo URL source. func WithSource(source string) Option { - return func(c *Client) { + return func(c *MatomoClient) { c.source = source } } -// New creates a new Matomo client. -func New(endpoint string, opts ...Option) Client { - c := Client{ +// NewMatomoClient creates a new Matomo client. +func NewMatomoClient(endpoint string, opts ...Option) MatomoClient { + c := MatomoClient{ endpoint: endpoint, source: endpoint, httpClient: http.Client{ @@ -161,7 +160,7 @@ func New(endpoint string, opts ...Option) Client { } // Send sends metric event to analytics. -func (c Client) Send(params Params) error { +func (c MatomoClient) Send(params MatomoParams) error { requestURL, err := url.Parse(c.endpoint) if err != nil { return err @@ -189,7 +188,7 @@ func (c Client) Send(params Params) error { } // SendMetric build the metrics and send to analytics. -func (c Client) SendMetric(sessionID string, metric Metric) error { +func (c MatomoClient) SendMetric(sessionID string, metric Metric) error { var ( now = time.Now() r, _ = rand.Int(rand.Reader, big.NewInt(math.MaxInt64)) @@ -201,7 +200,7 @@ func (c Client) SendMetric(sessionID string, metric Metric) error { cmd := splitCommand(metric.Cmd) - return c.Send(Params{ + return c.Send(MatomoParams{ IDSite: c.idSite, Rec: 1, APIVersion: 1, @@ -261,6 +260,6 @@ func splitCommand(cmd string) []string { } // metricURL build the metric URL. -func (c Client) metricURL(cmd string) string { +func (c MatomoClient) metricURL(cmd string) string { return fmt.Sprintf("%s/%s", c.source, strings.ReplaceAll(cmd, " ", "_")) } diff --git a/ignite/pkg/cosmoserror/error.go b/ignite/pkg/cosmoserror/error.go deleted file mode 100644 index e88c299d5d..0000000000 --- a/ignite/pkg/cosmoserror/error.go +++ /dev/null @@ -1,40 +0,0 @@ -package cosmoserror - -import ( - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - - "github.com/ignite/cli/v29/ignite/pkg/errors" -) - -var ( - ErrInternal = errors.New("internal error") - ErrInvalidRequest = errors.New("invalid request") - ErrNotFound = errors.New("not found") -) - -// Unwrap checks if an error contains a given grpc error code and returns the corresponding simple error type. -// -//nolint:exhaustive -func Unwrap(err error) error { - s, ok := status.FromError(err) - if ok { - switch s.Code() { - case codes.NotFound: - return ErrNotFound - case codes.InvalidArgument: - return ErrInvalidRequest - case codes.Internal: - return ErrInternal - } - } - if err := errors.Unwrap(err); err != nil { - return err - } - return err -} - -// IsNotFound returns if the given error is "not found". -func IsNotFound(err error) bool { - return errors.Is(Unwrap(err), ErrNotFound) -} diff --git a/ignite/pkg/cosmoserror/error_test.go b/ignite/pkg/cosmoserror/error_test.go deleted file mode 100644 index bcf112808f..0000000000 --- a/ignite/pkg/cosmoserror/error_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package cosmoserror_test - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/require" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - - "github.com/ignite/cli/v29/ignite/pkg/cosmoserror" - "github.com/ignite/cli/v29/ignite/pkg/errors" -) - -func TestUnwrap(t *testing.T) { - tests := []struct { - name string - err error - want error - }{ - { - name: "should return internal error", - err: status.Error(codes.Internal, "test error 1"), - want: cosmoserror.ErrInternal, - }, - { - name: "should return invalid request", - err: status.Error(codes.InvalidArgument, "test error 2"), - want: cosmoserror.ErrInvalidRequest, - }, - { - name: "should return not found", - err: status.Error(codes.NotFound, "test error 3"), - want: cosmoserror.ErrNotFound, - }, - { - name: "should return not found with wrapped error", - err: errors.Errorf("oups: %w", status.Error(codes.NotFound, "test error 4")), - want: cosmoserror.ErrNotFound, - }, - { - name: "should return same error", - err: errors.New("test error 5"), - want: errors.New("test error 5"), - }, - { - name: "should unwrap error", - err: fmt.Errorf("test error 4: %w", errors.New("test error 6")), //nolint:forbidigo - want: errors.New("test error 6"), - }, - } - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - unwrapped := cosmoserror.Unwrap(tc.err) - require.Equal(t, tc.want.Error(), unwrapped.Error()) - }) - } -} - -func TestIsNotFound(t *testing.T) { - tests := []struct { - name string - err error - want bool - }{ - { - name: "should return false from invalid code", - err: status.Error(codes.Internal, "test error 1"), - want: false, - }, - { - name: "should return false from invalid error", - err: errors.New("test error 4"), - want: false, - }, - { - name: "should return true", - err: status.Error(codes.NotFound, "test error 3"), - want: true, - }, - } - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - got := cosmoserror.IsNotFound(tc.err) - require.Equal(t, tc.want, got) - }) - } -} diff --git a/ignite/pkg/cosmostestutil/sample/sample.go b/ignite/pkg/cosmostestutil/sample/sample.go deleted file mode 100644 index bfead2af83..0000000000 --- a/ignite/pkg/cosmostestutil/sample/sample.go +++ /dev/null @@ -1,13 +0,0 @@ -package cosmostestutilsample - -import ( - "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// AccAddress returns a sample account address. -func AccAddress() string { - pk := ed25519.GenPrivKey().PubKey() - addr := pk.Address() - return sdk.AccAddress(addr).String() -} diff --git a/ignite/pkg/cosmostestutil/sample/sample_test.go b/ignite/pkg/cosmostestutil/sample/sample_test.go deleted file mode 100644 index 6fd158efe0..0000000000 --- a/ignite/pkg/cosmostestutil/sample/sample_test.go +++ /dev/null @@ -1,16 +0,0 @@ -package cosmostestutilsample - -import ( - "testing" - - addresscodec "github.com/cosmos/cosmos-sdk/codec/address" - "github.com/stretchr/testify/require" -) - -func TestAccAddress(t *testing.T) { - got := AccAddress() - require.NotEmpty(t, got) - exampleAccountAddress := addresscodec.NewBech32Codec("cosmos") - _, err := exampleAccountAddress.StringToBytes(got) - require.NoError(t, err) -} diff --git a/ignite/pkg/ctxreader/ctxreader.go b/ignite/pkg/ctxreader/ctxreader.go deleted file mode 100644 index e195ecaade..0000000000 --- a/ignite/pkg/ctxreader/ctxreader.go +++ /dev/null @@ -1,51 +0,0 @@ -// Package ctxreader brings context.Context to io.Reader -package ctxreader - -import ( - "context" - "io" - "sync" -) - -type cancelableReader struct { - io.Reader - ctx context.Context - m sync.Mutex - err error -} - -// New returns a new reader that emits a context error through its r.Read() method -// when ctx canceled. -func New(ctx context.Context, r io.Reader) io.Reader { - return &cancelableReader{Reader: r, ctx: ctx} -} - -// Read implements io.Reader and it stops blocking when reading is completed -// or context is cancelled. -func (r *cancelableReader) Read(data []byte) (n int, err error) { - r.m.Lock() - defer r.m.Unlock() - - if r.err != nil { - return 0, r.err - } - - var ( - readerN int - readerErr error - ) - isRead := make(chan struct{}) - go func() { - readerN, readerErr = r.Reader.Read(data) - close(isRead) - }() - - select { - case <-r.ctx.Done(): - r.err = r.ctx.Err() - return 0, r.ctx.Err() - case <-isRead: - r.err = readerErr - return readerN, readerErr - } -} diff --git a/ignite/pkg/ctxreader/ctxreader_test.go b/ignite/pkg/ctxreader/ctxreader_test.go deleted file mode 100644 index 6a146378c4..0000000000 --- a/ignite/pkg/ctxreader/ctxreader_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package ctxreader - -import ( - "bytes" - "context" - "testing" - - "github.com/stretchr/testify/require" -) - -func TestReadAndCancel(t *testing.T) { - // create the ctx. - ctx, cancel := context.WithCancel(context.Background()) - - // create a buffer and write some initial data. - buf := &bytes.Buffer{} - buf.Write([]byte{1, 2, 3}) - - // initialize cancelableReader with buf. - r := New(ctx, buf) - - // make sure that cancelableReader will read the first 2 bytes - // of previously written data and. - data := make([]byte, 2) - - n, err := r.Read(data) - require.NoError(t, err) - require.Equal(t, 2, n) - require.Equal(t, []byte{1, 2}, data) - - // cancel ctx and try to read again, this time Read will unblock and return - // with a context.Canceled instead of reading the 3rd byte from initial data. - cancel() - - n, err = r.Read(data) - require.Equal(t, context.Canceled, err) - require.Equal(t, 0, n) -} diff --git a/ignite/pkg/numbers/numbers.go b/ignite/pkg/numbers/numbers.go deleted file mode 100644 index c5297787df..0000000000 --- a/ignite/pkg/numbers/numbers.go +++ /dev/null @@ -1,90 +0,0 @@ -package numbers - -import ( - "fmt" - "strconv" - "strings" - - "github.com/ignite/cli/v29/ignite/pkg/errors" -) - -const ( - separator = "," - sepRange = "-" -) - -// ParseList parses comma separated numbers and range to []uint64. -func ParseList(arg string) ([]uint64, error) { - result := make([]uint64, 0) - listNumbers := make(map[uint64]struct{}) - // Split the slice by the separator - for _, numberRange := range strings.Split(arg, separator) { - trimmedRange := strings.TrimSpace(numberRange) - if trimmedRange == "" { - continue - } - - // Split the number by the separator range - numbers := strings.Split(trimmedRange, sepRange) - switch len(numbers) { - // Parse a single number - case 1: - trimmed := strings.TrimSpace(numbers[0]) - i, err := strconv.ParseUint(trimmed, 10, 32) - if err != nil { - return nil, err - } - if _, ok := listNumbers[i]; ok { - continue - } - listNumbers[i] = struct{}{} - result = append(result, i) - - // Parse a range number (eg: 3-7) - case 2: - var ( - startN = strings.TrimSpace(numbers[0]) - endN = strings.TrimSpace(numbers[1]) - ) - if startN == "" { - startN = endN - } - if endN == "" { - endN = startN - } - if startN == "" { - continue - } - start, err := strconv.ParseUint(startN, 10, 32) - if err != nil { - return nil, err - } - end, err := strconv.ParseUint(endN, 10, 32) - if err != nil { - return nil, err - } - if start > end { - return nil, errors.Errorf("cannot parse a reverse ordering range: %s", trimmedRange) - } - for ; start <= end; start++ { - if _, ok := listNumbers[start]; ok { - continue - } - listNumbers[start] = struct{}{} - result = append(result, start) - } - default: - return nil, errors.Errorf("cannot parse the number range: %s", trimmedRange) - } - } - return result, nil -} - -// List creates a comma separated int list with optional prefix for each uint64. -func List(numbers []uint64, prefix string) string { - var s []string - for _, n := range numbers { - s = append(s, fmt.Sprintf("%s%d", prefix, n)) - } - return strings.Join(s, ", ") -} diff --git a/ignite/pkg/numbers/numbers_test.go b/ignite/pkg/numbers/numbers_test.go deleted file mode 100644 index 21025c6060..0000000000 --- a/ignite/pkg/numbers/numbers_test.go +++ /dev/null @@ -1,73 +0,0 @@ -package numbers - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/ignite/cli/v29/ignite/pkg/errors" -) - -func TestParseList(t *testing.T) { - cases := []struct { - list string - parsed []uint64 - }{ - {"1,2,3", []uint64{1, 2, 3}}, - {"1, 2,3 ", []uint64{1, 2, 3}}, - {",1, 2,", []uint64{1, 2}}, - {"1-3 ", []uint64{1, 2, 3}}, - {"1-3,8 ", []uint64{1, 2, 3, 8}}, - {"8-11, 1-3, ", []uint64{8, 9, 10, 11, 1, 2, 3}}, - {"1-3,33, 8-11, ", []uint64{1, 2, 3, 33, 8, 9, 10, 11}}, - {"1-3,8-11,33-36 ", []uint64{1, 2, 3, 8, 9, 10, 11, 33, 34, 35, 36}}, - {"2-7,2-5,9-11,1-8", []uint64{2, 3, 4, 5, 6, 7, 9, 10, 11, 1, 8}}, - {",", []uint64{}}, - {",-", []uint64{}}, - {",10-", []uint64{10}}, - {"10-", []uint64{10}}, - {"-10", []uint64{10}}, - {"10-10", []uint64{10}}, - } - for _, tt := range cases { - t.Run("list "+tt.list, func(t *testing.T) { - parsed, err := ParseList(tt.list) - require.NoError(t, err) - require.Equal(t, tt.parsed, parsed) - }) - } -} - -func TestParseListErrors(t *testing.T) { - cases := []struct { - list string - err error - }{ - {"12-8", errors.New("cannot parse a reverse ordering range: 12-8")}, - {"1-2-3", errors.New("cannot parse the number range: 1-2-3")}, - } - for _, tt := range cases { - t.Run("list "+tt.list, func(t *testing.T) { - _, err := ParseList(tt.list) - require.Error(t, err) - require.True(t, errors.Is(tt.err, err)) - }) - } -} - -func TestList(t *testing.T) { - cases := []struct { - parsed []uint64 - list string - }{ - {[]uint64{1, 2, 3}, "#1, #2, #3"}, - {[]uint64{1}, "#1"}, - {[]uint64{}, ""}, - } - for i, tt := range cases { - t.Run(fmt.Sprintf("no: %d", i), func(t *testing.T) { - require.Equal(t, tt.list, List(tt.parsed, "#")) - }) - } -} diff --git a/ignite/pkg/tendermintrpc/rpc.go b/ignite/pkg/tendermintrpc/rpc.go deleted file mode 100644 index f9ac0e9efd..0000000000 --- a/ignite/pkg/tendermintrpc/rpc.go +++ /dev/null @@ -1,163 +0,0 @@ -package tendermintrpc - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "io" - "net/http" - "strconv" - - "github.com/ignite/cli/v29/ignite/pkg/errors" - "github.com/ignite/cli/v29/ignite/pkg/safeconverter" -) - -const ( - endpointNetInfo = "/net_info" - endpointGenesis = "/genesis" - endpointStatus = "/status" -) - -// Client is a Tendermint RPC client. -type Client struct { - addr string -} - -// New creates a new Tendermint RPC client. -func New(addr string) Client { - return Client{addr: addr} -} - -// NetInfo represents Network Info. -type NetInfo struct { - ConnectedPeers int -} - -func (c Client) url(endpoint string) string { - return fmt.Sprintf("%s%s", c.addr, endpoint) -} - -// GetNetInfo retrieves network info. -func (c Client) GetNetInfo(ctx context.Context) (NetInfo, error) { - req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.url(endpointNetInfo), nil) - if err != nil { - return NetInfo{}, err - } - resp, err := http.DefaultClient.Do(req) - if err != nil { - return NetInfo{}, err - } - defer resp.Body.Close() - - var res struct { - Result struct { - Peers string `json:"n_peers"` - } `json:"result"` - } - if err := json.NewDecoder(resp.Body).Decode(&res); err != nil { - return NetInfo{}, err - } - - peers, err := strconv.ParseUint(res.Result.Peers, 10, 64) - if err != nil { - return NetInfo{}, err - } - - return NetInfo{ - ConnectedPeers: safeconverter.ToInt[uint64](peers), - }, nil -} - -// Genesis represents Genesis. -type Genesis struct { - ChainID string `json:"chain_id"` -} - -// GetGenesis retrieves Genesis. -func (c Client) GetGenesis(ctx context.Context) (Genesis, error) { - req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.url(endpointGenesis), nil) - if err != nil { - return Genesis{}, err - } - - resp, err := http.DefaultClient.Do(req) - if err != nil { - return Genesis{}, err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return Genesis{}, errors.Errorf("%d", resp.StatusCode) - } - - var out struct { - Result struct { - Genesis Genesis `json:"genesis"` - } `json:"Result"` - } - - if err := json.NewDecoder(resp.Body).Decode(&out); err != nil { - return Genesis{}, err - } - - return out.Result.Genesis, nil -} - -// NodeInfo holds node info. -type NodeInfo struct { - Network string -} - -// Status retrieves node Status. -func (c Client) Status(ctx context.Context) (NodeInfo, error) { - req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.url(endpointStatus), nil) - if err != nil { - return NodeInfo{}, err - } - - resp, err := http.DefaultClient.Do(req) - if err != nil { - return NodeInfo{}, err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return NodeInfo{}, errors.Errorf("%d", resp.StatusCode) - } - - var ( - info NodeInfo - b = &bytes.Buffer{} - r = io.TeeReader(resp.Body, b) - ) - - var out struct { - Result struct { - NodeInfo NodeInfo `json:"node_info"` - } `json:"result"` - } - - if err := json.NewDecoder(r).Decode(&out); err != nil { - return NodeInfo{}, err - } - - info = out.Result.NodeInfo - - // some versions have a different response payload. - if info.Network == "" { - var out struct { - Result struct { - NodeInfo NodeInfo `json:"NodeInfo"` - } `json:"result"` - } - - if err := json.NewDecoder(b).Decode(&out); err != nil { - return NodeInfo{}, err - } - - info = out.Result.NodeInfo - } - - return info, nil -} From b15df74f6ed884958f2ea8487f7a992e64865f6b Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Thu, 25 Sep 2025 23:42:50 +0200 Subject: [PATCH 08/11] updates --- ignite/cmd/ignite/main.go | 3 +- ignite/cmd/scaffold_module.go | 3 +- ignite/pkg/cosmosutil/address.go | 25 ---- ignite/pkg/cosmosutil/address_test.go | 81 ------------ ignite/pkg/cosmosutil/config.go | 5 - ignite/pkg/cosmosutil/genesis/genesis.go | 118 ------------------ ignite/pkg/cosmosutil/genesis/genesis_test.go | 38 ------ ignite/pkg/cosmosutil/gentx.go | 101 --------------- ignite/pkg/cosmosutil/gentx_test.go | 74 ----------- ignite/pkg/cosmosutil/testdata/gentx1.json | 61 --------- ignite/pkg/cosmosutil/testdata/gentx2.json | 66 ---------- .../cosmosutil/testdata/gentx_invalid.json | 6 - ignite/pkg/errors/errors.go | 7 ++ ignite/pkg/placeholder/error.go | 7 +- ignite/pkg/validation/errors.go | 7 -- ignite/services/scaffolder/module.go | 3 +- 16 files changed, 13 insertions(+), 592 deletions(-) delete mode 100644 ignite/pkg/cosmosutil/address.go delete mode 100644 ignite/pkg/cosmosutil/address_test.go delete mode 100644 ignite/pkg/cosmosutil/config.go delete mode 100644 ignite/pkg/cosmosutil/genesis/genesis.go delete mode 100644 ignite/pkg/cosmosutil/genesis/genesis_test.go delete mode 100644 ignite/pkg/cosmosutil/gentx.go delete mode 100644 ignite/pkg/cosmosutil/gentx_test.go delete mode 100644 ignite/pkg/cosmosutil/testdata/gentx1.json delete mode 100644 ignite/pkg/cosmosutil/testdata/gentx2.json delete mode 100644 ignite/pkg/cosmosutil/testdata/gentx_invalid.json create mode 100644 ignite/pkg/errors/errors.go delete mode 100644 ignite/pkg/validation/errors.go diff --git a/ignite/cmd/ignite/main.go b/ignite/cmd/ignite/main.go index cce8d18b8a..e4d5eeb3bb 100644 --- a/ignite/cmd/ignite/main.go +++ b/ignite/cmd/ignite/main.go @@ -18,7 +18,6 @@ import ( "github.com/ignite/cli/v29/ignite/pkg/cliui/colors" "github.com/ignite/cli/v29/ignite/pkg/cliui/icons" "github.com/ignite/cli/v29/ignite/pkg/errors" - "github.com/ignite/cli/v29/ignite/pkg/validation" "github.com/ignite/cli/v29/ignite/pkg/xstrings" "github.com/ignite/cli/v29/ignite/version" ) @@ -64,7 +63,7 @@ func run() int { if err != nil { var ( - validationErr validation.Error + validationErr errors.ValidationError versionErr chainconfig.VersionError msg string ) diff --git a/ignite/cmd/scaffold_module.go b/ignite/cmd/scaffold_module.go index d7ab7733de..ca92be8534 100644 --- a/ignite/cmd/scaffold_module.go +++ b/ignite/cmd/scaffold_module.go @@ -10,7 +10,6 @@ import ( "github.com/ignite/cli/v29/ignite/pkg/cliui" "github.com/ignite/cli/v29/ignite/pkg/errors" - "github.com/ignite/cli/v29/ignite/pkg/validation" "github.com/ignite/cli/v29/ignite/pkg/xgenny" "github.com/ignite/cli/v29/ignite/services/scaffolder" modulecreate "github.com/ignite/cli/v29/ignite/templates/module/create" @@ -182,7 +181,7 @@ func scaffoldModuleHandler(cmd *cobra.Command, args []string) error { } if err := sc.CreateModule(name, options...); err != nil { - var validationErr validation.Error + var validationErr errors.ValidationError if !requireRegistration && errors.As(err, &validationErr) { fmt.Fprintf(&msg, "Can't register module '%s'.\n", name) fmt.Fprintln(&msg, validationErr.ValidationInfo()) diff --git a/ignite/pkg/cosmosutil/address.go b/ignite/pkg/cosmosutil/address.go deleted file mode 100644 index 8ea9ee3192..0000000000 --- a/ignite/pkg/cosmosutil/address.go +++ /dev/null @@ -1,25 +0,0 @@ -package cosmosutil - -import ( - "github.com/cosmos/cosmos-sdk/types/bech32" - - "github.com/ignite/cli/v29/ignite/pkg/errors" -) - -// ChangeAddressPrefix returns the address with another prefix. -func ChangeAddressPrefix(address, newPrefix string) (string, error) { - if newPrefix == "" { - return "", errors.New("empty prefix") - } - _, pubKey, err := bech32.DecodeAndConvert(address) - if err != nil { - return "", err - } - return bech32.ConvertAndEncode(newPrefix, pubKey) -} - -// GetAddressPrefix returns the bech 32 prefix used by the address. -func GetAddressPrefix(address string) (string, error) { - prefix, _, err := bech32.DecodeAndConvert(address) - return prefix, err -} diff --git a/ignite/pkg/cosmosutil/address_test.go b/ignite/pkg/cosmosutil/address_test.go deleted file mode 100644 index 2d065fc4f3..0000000000 --- a/ignite/pkg/cosmosutil/address_test.go +++ /dev/null @@ -1,81 +0,0 @@ -package cosmosutil_test - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/ignite/cli/v29/ignite/pkg/cosmosutil" -) - -func TestChangePrefix(t *testing.T) { - tests := []struct { - name string - address string - prefix string - want string - wantErr bool - }{ - { - name: "cosmos address to spn address", - address: "cosmos1dd246yq6z5vzjz9gh8cff46pll75yyl8ygndsj", - prefix: "spn", - want: "spn1dd246yq6z5vzjz9gh8cff46pll75yyl8c5tt7g", - }, - { - name: "cosmos address to spn address 2", - address: "cosmos1mmlqwyqk7neqegffp99q86eckpm4pjah3ytlpa", - prefix: "spn", - want: "spn1mmlqwyqk7neqegffp99q86eckpm4pjahdcne08", - }, - { - name: "cosmos validator address", - address: "cosmosvaloper1mmlqwyqk7neqegffp99q86eckpm4pjah5sl2dw", - prefix: "spn", - want: "spn1mmlqwyqk7neqegffp99q86eckpm4pjahdcne08", - }, - { - name: "mars address to earth address", - address: "mars1c6ac48k2ur8tl3tf0cpntlw5068kvp8xf4xq37", - prefix: "earth", - want: "earth1c6ac48k2ur8tl3tf0cpntlw5068kvp8x0xyl2v", - }, - { - name: "invalid bech32 address", - address: "mars1c6ac48k2ur9tl3tf0cpntlw5068kvp8xf4xq37", - prefix: "spn", - wantErr: true, - }, - { - name: "empty target prefix", - address: "mars1c6ac48k2ur8tl3tf0cpntlw5068kvp8xf4xq37", - prefix: "", - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := cosmosutil.ChangeAddressPrefix(tt.address, tt.prefix) - if tt.wantErr { - require.Error(t, err) - return - } - require.NoError(t, err) - require.Equal(t, tt.want, got) - }) - } -} - -func TestGetPrefix(t *testing.T) { - prefix, err := cosmosutil.GetAddressPrefix("cosmos1dd246yq6z5vzjz9gh8cff46pll75yyl8ygndsj") - require.Equal(t, "cosmos", prefix) - require.NoError(t, err) - - prefix, err = cosmosutil.GetAddressPrefix("mars1c6ac48k2ur8tl3tf0cpntlw5068kvp8xf4xq37") - require.Equal(t, "mars", prefix) - require.NoError(t, err) - - // invalid bech32 address - _, err = cosmosutil.GetAddressPrefix("mars1c6ac48k2ur9tl3tf0cpntlw5068kvp8xf4xq37") - require.Error(t, err) -} diff --git a/ignite/pkg/cosmosutil/config.go b/ignite/pkg/cosmosutil/config.go deleted file mode 100644 index aed442059f..0000000000 --- a/ignite/pkg/cosmosutil/config.go +++ /dev/null @@ -1,5 +0,0 @@ -package cosmosutil - -const ( - ChainConfigDir = "config" -) diff --git a/ignite/pkg/cosmosutil/genesis/genesis.go b/ignite/pkg/cosmosutil/genesis/genesis.go deleted file mode 100644 index 6e0979ffbf..0000000000 --- a/ignite/pkg/cosmosutil/genesis/genesis.go +++ /dev/null @@ -1,118 +0,0 @@ -package genesis - -import ( - "context" - "fmt" - "os" - "slices" - - "github.com/ignite/cli/v29/ignite/pkg/jsonfile" -) - -const ( - genesisFilename = "genesis.json" - - fieldPathStakeDenom = "app_state.staking.params.bond_denom" - fieldPathChainID = "chain_id" - fieldPathAccounts = "app_state.auth.accounts" - fieldPathGentxs = "app_state.genutil.gen_txs" - - FieldGenesisTime = "genesis_time" - FieldChainID = "chain_id" - FieldConsumerChainID = "app_state.monitoringp.params.consumerChainID" - FieldLastBlockHeight = "app_state.monitoringp.params.lastBlockHeight" - FieldConsensusTimestamp = "app_state.monitoringp.params.consumerConsensusState.timestamp" - FieldConsensusNextValidatorsHash = "app_state.monitoringp.params.consumerConsensusState.nextValidatorsHash" - FieldConsensusRootHash = "app_state.monitoringp.params.consumerConsensusState.root.hash" - FieldConsumerUnbondingPeriod = "app_state.monitoringp.params.consumerUnbondingPeriod" - FieldConsumerRevisionHeight = "app_state.monitoringp.params.consumerRevisionHeight" - - fieldModuleParamFormatString = "app_state.%s.params.%s" -) - -type ( - // Genesis represents the genesis reader. - Genesis struct { - *jsonfile.JSONFile - } - accounts []struct { - Address string `json:"address"` - } - gentxs []struct{} -) - -// ModuleParamField returns the field name of a given module param pair. -func ModuleParamField(module, param string) string { - return fmt.Sprintf(fieldModuleParamFormatString, module, param) -} - -// FromPath parses genesis object from path. -func FromPath(path string) (*Genesis, error) { - file, err := jsonfile.FromPath(path) - return &Genesis{ - JSONFile: file, - }, err -} - -// FromURL fetches the genesis from the given URL and returns its content. -func FromURL(ctx context.Context, url, path string) (*Genesis, error) { - file, err := jsonfile.FromURL(ctx, url, path, genesisFilename) - return &Genesis{ - JSONFile: file, - }, err -} - -// CheckGenesisContainsAddress returns true if the address exist into the genesis file. -func CheckGenesisContainsAddress(genesisPath, addr string) (bool, error) { - _, err := os.Stat(genesisPath) - if os.IsNotExist(err) { - return false, nil - } else if err != nil { - return false, err - } - genesis, err := FromPath(genesisPath) - if err != nil { - return false, err - } - defer genesis.Close() - return genesis.HasAccount(addr), nil -} - -// HasAccount check if account exist into the genesis account. -func (g Genesis) HasAccount(address string) bool { - accounts, err := g.Accounts() - if err != nil { - return false - } - return slices.Contains(accounts, address) -} - -// StakeDenom returns the stake denom from the genesis. -func (g *Genesis) StakeDenom() (denom string, err error) { - err = g.Field(fieldPathStakeDenom, &denom) - return -} - -// ChainID returns the chain id from the genesis. -func (g *Genesis) ChainID() (chainID string, err error) { - err = g.Field(fieldPathChainID, &chainID) - return -} - -// Accounts returns the auth accounts from the genesis. -func (g *Genesis) Accounts() ([]string, error) { - var accs accounts - err := g.Field(fieldPathAccounts, &accs) - accountList := make([]string, len(accs)) - for i, acc := range accs { - accountList[i] = acc.Address - } - return accountList, err -} - -// GentxCount returns the number of gentx in the genesis. -func (g *Genesis) GentxCount() (int, error) { - var gentxs gentxs - err := g.Field(fieldPathGentxs, &gentxs) - return len(gentxs), err -} diff --git a/ignite/pkg/cosmosutil/genesis/genesis_test.go b/ignite/pkg/cosmosutil/genesis/genesis_test.go deleted file mode 100644 index a66fa452ce..0000000000 --- a/ignite/pkg/cosmosutil/genesis/genesis_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package genesis_test - -import ( - "testing" - - "github.com/stretchr/testify/require" - - cosmosgenesis "github.com/ignite/cli/v29/ignite/pkg/cosmosutil/genesis" -) - -func TestModuleParamField(t *testing.T) { - tests := []struct { - name string - module string - param string - want string - }{ - { - name: "valid 1", - module: "foo", - param: "bar", - want: "app_state.foo.params.bar", - }, - { - name: "valid 2", - module: "bar", - param: "foo", - want: "app_state.bar.params.foo", - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - got := cosmosgenesis.ModuleParamField(tc.module, tc.param) - require.Equal(t, tc.want, got) - }) - } -} diff --git a/ignite/pkg/cosmosutil/gentx.go b/ignite/pkg/cosmosutil/gentx.go deleted file mode 100644 index 2d16b7e624..0000000000 --- a/ignite/pkg/cosmosutil/gentx.go +++ /dev/null @@ -1,101 +0,0 @@ -package cosmosutil - -import ( - "encoding/base64" - "encoding/json" - "os" - - sdkmath "cosmossdk.io/math" - - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/cometbft/cometbft/crypto/ed25519" - - "github.com/ignite/cli/v29/ignite/pkg/errors" -) - -type ( - // GentxInfo represents the basic info about gentx file. - GentxInfo struct { - // Deprecated: Use of Delegator Address in MsgCreateValidator is deprecated. - // The validator address bytes and delegator address bytes refer to the same account while creating validator (defer - // only in bech32 notation). - DelegatorAddress string - ValidatorAddress string - PubKey ed25519.PubKey - SelfDelegation sdk.Coin - Memo string - } - - // Gentx represents the gentx file. - Gentx struct { - Body struct { - Messages []struct { - DelegatorAddress string `json:"delegator_address"` - ValidatorAddress string `json:"validator_address"` - PubKey struct { - Type string `json:"@type"` - Key string `json:"key"` - } `json:"pubkey"` - Value struct { - Denom string `json:"denom"` - Amount string `json:"amount"` - } `json:"value"` - } `json:"messages"` - Memo string `json:"memo"` - } `json:"body"` - } -) - -// GentxFromPath returns GentxInfo from the json file. -func GentxFromPath(path string) (info GentxInfo, gentx []byte, err error) { - if _, err := os.Stat(path); os.IsNotExist(err) { - return info, gentx, errors.New("chain home folder is not initialized yet: " + path) - } - - gentx, err = os.ReadFile(path) - if err != nil { - return info, gentx, err - } - - info, err = ParseGentx(gentx) - return info, gentx, err -} - -// ParseGentx returns GentxInfo and the gentx file in bytes. -func ParseGentx(gentxBz []byte) (info GentxInfo, err error) { - // Try parsing gentx - var gentx Gentx - if err := json.Unmarshal(gentxBz, &gentx); err != nil { - return info, errors.Errorf("unmarshal gentx: %w", err) - } - if gentx.Body.Messages == nil { - return info, errors.New("the gentx cannot be parsed") - } - - if len(gentx.Body.Messages) != 1 { - return info, errors.New("add validator gentx must contain 1 message") - } - - info.Memo = gentx.Body.Memo - info.DelegatorAddress = gentx.Body.Messages[0].DelegatorAddress - info.ValidatorAddress = gentx.Body.Messages[0].ValidatorAddress - - pb := gentx.Body.Messages[0].PubKey.Key - info.PubKey, err = base64.StdEncoding.DecodeString(pb) - if err != nil { - return info, errors.Errorf("invalid validator public key %w", err) - } - - amount, ok := sdkmath.NewIntFromString(gentx.Body.Messages[0].Value.Amount) - if !ok { - return info, errors.New("the self-delegation inside the gentx is invalid") - } - - info.SelfDelegation = sdk.NewCoin( - gentx.Body.Messages[0].Value.Denom, - amount, - ) - - return info, nil -} diff --git a/ignite/pkg/cosmosutil/gentx_test.go b/ignite/pkg/cosmosutil/gentx_test.go deleted file mode 100644 index 0c890c32d6..0000000000 --- a/ignite/pkg/cosmosutil/gentx_test.go +++ /dev/null @@ -1,74 +0,0 @@ -package cosmosutil_test - -import ( - "encoding/base64" - "testing" - - sdkmath "cosmossdk.io/math" - "github.com/cometbft/cometbft/crypto/ed25519" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/stretchr/testify/require" - - "github.com/ignite/cli/v29/ignite/pkg/cosmosutil" -) - -func TestParseGentx(t *testing.T) { - pk1, err := base64.StdEncoding.DecodeString("aeQLCJOjXUyB7evOodI4mbrshIt3vhHGlycJDbUkaMs=") - require.NoError(t, err) - pk2, err := base64.StdEncoding.DecodeString("OL+EIoo7DwyaBFDbPbgAhwS5rvgIqoUa0x8qWqzfQVQ=") - require.NoError(t, err) - - tests := []struct { - name string - gentxPath string - wantInfo cosmosutil.GentxInfo - wantErr bool - }{ - { - name: "parse gentx file 1", - gentxPath: "testdata/gentx1.json", - wantInfo: cosmosutil.GentxInfo{ - DelegatorAddress: "cosmos1dd246yq6z5vzjz9gh8cff46pll75yyl8ygndsj", - ValidatorAddress: "cosmosvaloper1dd246yq6z5vzjz9gh8cff46pll75yyl8pu8cup", - PubKey: ed25519.PubKey(pk1), - SelfDelegation: sdk.Coin{ - Denom: "stake", - Amount: sdkmath.NewInt(95000000), - }, - Memo: "9b1f4adbfb0c0b513040d914bfb717303c0eaa71@192.168.0.148:26656", - }, - }, { - name: "parse gentx file 2", - gentxPath: "testdata/gentx2.json", - wantInfo: cosmosutil.GentxInfo{ - DelegatorAddress: "", - ValidatorAddress: "cosmosvaloper1mmlqwyqk7neqegffp99q86eckpm4pjah5sl2dw", - PubKey: ed25519.PubKey(pk2), - SelfDelegation: sdk.Coin{ - Denom: "stake", - Amount: sdkmath.NewInt(95000000), - }, - Memo: "a412c917cb29f73cc3ad0592bbd0152fe0e690bd@192.168.0.148:26656", - }, - }, { - name: "parse invalid file", - gentxPath: "testdata/gentx_invalid.json", - wantErr: true, - }, { - name: "not found file", - gentxPath: "testdata/gentx_not_found.json", - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotInfo, _, err := cosmosutil.GentxFromPath(tt.gentxPath) - if tt.wantErr { - require.Error(t, err) - return - } - require.NoError(t, err) - require.Equal(t, tt.wantInfo, gotInfo) - }) - } -} diff --git a/ignite/pkg/cosmosutil/testdata/gentx1.json b/ignite/pkg/cosmosutil/testdata/gentx1.json deleted file mode 100644 index 3b4aecdefa..0000000000 --- a/ignite/pkg/cosmosutil/testdata/gentx1.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "auth_info": { - "fee": { - "amount": [], - "gas_limit": "200000", - "granter": "", - "payer": "" - }, - "signer_infos": [ - { - "mode_info": { - "single": { - "mode": "SIGN_MODE_DIRECT" - } - }, - "public_key": { - "@type": "/cosmos.crypto.secp256k1.PubKey", - "key": "AhLlX8QQEymFlvdKrb0xfYGHt7GTK8KiExAThDHQKSe4" - }, - "sequence": "0" - } - ] - }, - "body": { - "extension_options": [], - "memo": "9b1f4adbfb0c0b513040d914bfb717303c0eaa71@192.168.0.148:26656", - "messages": [ - { - "@type": "/cosmos.staking.v1beta1.MsgCreateValidator", - "commission": { - "max_change_rate": "0.010000000000000000", - "max_rate": "0.200000000000000000", - "rate": "0.100000000000000000" - }, - "delegator_address": "cosmos1dd246yq6z5vzjz9gh8cff46pll75yyl8ygndsj", - "description": { - "details": "", - "identity": "", - "moniker": "default", - "security_contact": "", - "website": "" - }, - "min_self_delegation": "1", - "pubkey": { - "@type": "/cosmos.crypto.ed25519.PubKey", - "key": "aeQLCJOjXUyB7evOodI4mbrshIt3vhHGlycJDbUkaMs=" - }, - "validator_address": "cosmosvaloper1dd246yq6z5vzjz9gh8cff46pll75yyl8pu8cup", - "value": { - "amount": "95000000", - "denom": "stake" - } - } - ], - "non_critical_extension_options": [], - "timeout_height": "0" - }, - "signatures": [ - "sz0uixBOHJoZbvVrz670vLBRQ5Z2wnhHeNRxKJPz5dADKfz34/sg7FQv6nCeEomODMrgjUD70YBeguKIqxjcLw==" - ] -} \ No newline at end of file diff --git a/ignite/pkg/cosmosutil/testdata/gentx2.json b/ignite/pkg/cosmosutil/testdata/gentx2.json deleted file mode 100644 index 24357d5a17..0000000000 --- a/ignite/pkg/cosmosutil/testdata/gentx2.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "auth_info": { - "fee": { - "amount": [ - { - "amount": "5000", - "denom": "stake" - } - ], - "gas_limit": "200000", - "granter": "", - "payer": "" - }, - "signer_infos": [ - { - "mode_info": { - "single": { - "mode": "SIGN_MODE_DIRECT" - } - }, - "public_key": { - "@type": "/cosmos.crypto.secp256k1.PubKey", - "key": "AslH/zmmjEHI/jWup3tC/TfG4eRiD959tyE9z98xt/oO" - }, - "sequence": "0" - } - ] - }, - "body": { - "extension_options": [], - "memo": "a412c917cb29f73cc3ad0592bbd0152fe0e690bd@192.168.0.148:26656", - "messages": [ - { - "@type": "/cosmos.staking.v1beta1.MsgCreateValidator", - "commission": { - "max_change_rate": "0.010000000000000000", - "max_rate": "0.200000000000000000", - "rate": "0.100000000000000000" - }, - "delegator_address": "", - "description": { - "details": "", - "identity": "", - "moniker": "alice", - "security_contact": "", - "website": "" - }, - "min_self_delegation": "1", - "pubkey": { - "@type": "/cosmos.crypto.ed25519.PubKey", - "key": "OL+EIoo7DwyaBFDbPbgAhwS5rvgIqoUa0x8qWqzfQVQ=" - }, - "validator_address": "cosmosvaloper1mmlqwyqk7neqegffp99q86eckpm4pjah5sl2dw", - "value": { - "amount": "95000000", - "denom": "stake" - } - } - ], - "non_critical_extension_options": [], - "timeout_height": "0" - }, - "signatures": [ - "XDwkcX6QNRDL7FYD/UCNXmwVZHR7tCVyTh+VKAC8KJESZyCEo4/Uo9HGRX2pWGX0nrn/v5h2HKzHxXc/41rDag==" - ] -} \ No newline at end of file diff --git a/ignite/pkg/cosmosutil/testdata/gentx_invalid.json b/ignite/pkg/cosmosutil/testdata/gentx_invalid.json deleted file mode 100644 index e3153028f8..0000000000 --- a/ignite/pkg/cosmosutil/testdata/gentx_invalid.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "amount": [], - "gas_limit": "200000", - "granter": "", - "payer": "" -} \ No newline at end of file diff --git a/ignite/pkg/errors/errors.go b/ignite/pkg/errors/errors.go new file mode 100644 index 0000000000..6ce18a6df5 --- /dev/null +++ b/ignite/pkg/errors/errors.go @@ -0,0 +1,7 @@ +package errors + +// ValidationError must be implemented by errors that provide validation info. +type ValidationError interface { + error + ValidationInfo() string +} diff --git a/ignite/pkg/placeholder/error.go b/ignite/pkg/placeholder/error.go index d963cf1c15..fe87caefca 100644 --- a/ignite/pkg/placeholder/error.go +++ b/ignite/pkg/placeholder/error.go @@ -5,10 +5,9 @@ import ( "strings" "github.com/ignite/cli/v29/ignite/pkg/errors" - "github.com/ignite/cli/v29/ignite/pkg/validation" ) -var _ validation.Error = (*MissingPlaceholdersError)(nil) +var _ errors.ValidationError = (*MissingPlaceholdersError)(nil) // MissingPlaceholdersError is used as an error when a source file is missing placeholder. type MissingPlaceholdersError struct { @@ -70,7 +69,7 @@ func (e *MissingPlaceholdersError) ValidationInfo() string { return b.String() } -var _ validation.Error = (*ValidationMiscError)(nil) +var _ errors.ValidationError = (*ValidationMiscError)(nil) // ValidationMiscError is used as a miscellaneous error related to validation. type ValidationMiscError struct { @@ -82,7 +81,7 @@ func (e *ValidationMiscError) Error() string { return fmt.Sprintf("validation errors: %v", e.errors) } -// ValidationInfo implements validation.Error interface. +// ValidationInfo implements errors.ValidationError interface. func (e *ValidationMiscError) ValidationInfo() string { return fmt.Sprintf("Validation errors:\n\n%v", strings.Join(e.errors, "\n")) } diff --git a/ignite/pkg/validation/errors.go b/ignite/pkg/validation/errors.go deleted file mode 100644 index 345ad9de5c..0000000000 --- a/ignite/pkg/validation/errors.go +++ /dev/null @@ -1,7 +0,0 @@ -package validation - -// Error must be implemented by errors that provide validation info. -type Error interface { - error - ValidationInfo() string -} diff --git a/ignite/services/scaffolder/module.go b/ignite/services/scaffolder/module.go index 8d205cdeea..a4151da2fe 100644 --- a/ignite/services/scaffolder/module.go +++ b/ignite/services/scaffolder/module.go @@ -11,7 +11,6 @@ import ( appanalysis "github.com/ignite/cli/v29/ignite/pkg/cosmosanalysis/app" "github.com/ignite/cli/v29/ignite/pkg/errors" "github.com/ignite/cli/v29/ignite/pkg/multiformatname" - "github.com/ignite/cli/v29/ignite/pkg/validation" "github.com/ignite/cli/v29/ignite/templates/field" "github.com/ignite/cli/v29/ignite/templates/module" modulecreate "github.com/ignite/cli/v29/ignite/templates/module/create" @@ -232,7 +231,7 @@ func (s Scaffolder) CreateModule( gens = append(gens, modulecreate.NewAppModify(s.Tracer(), opts)) err = s.Run(gens...) - var validationErr validation.Error + var validationErr errors.ValidationError if err != nil && !errors.As(err, &validationErr) { return err } From c614c8e0df0c6d68d6b0a0ac86918ce1356d3fe3 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Thu, 25 Sep 2025 23:45:06 +0200 Subject: [PATCH 09/11] remove script --- scripts/indocker | 75 ------------------------------------------------ 1 file changed, 75 deletions(-) delete mode 100755 scripts/indocker diff --git a/scripts/indocker b/scripts/indocker deleted file mode 100755 index 0e0ffda8f7..0000000000 --- a/scripts/indocker +++ /dev/null @@ -1,75 +0,0 @@ -#!/bin/bash - -function build { - echo "building container..." - docker build -t ignite-cli -f - > /dev/null . << EOF -FROM golang:1.23.0 - -WORKDIR /apps - -RUN apt update && \ - apt-get install -y \ - build-essential \ - ca-certificates \ - unzip \ - curl \ - vim \ - git - -RUN curl -sL https://deb.nodesource.com/setup_14.x | bash - && \ - apt-get install -y nodejs - -RUN echo '\n\ -alias a="cd /apps" \n\ -alias s="cd /src " \n\ -alias c="(cd && GLOBIGNORE='.bashrc:.cache:.config:.npm:.profile'; rm -rf *) && rm -rf /apps/*" \n\ -alias i="(s && go install ./...)"' > ~/.bashrc -EOF -} - -function run { - docker kill ignite-cli > /dev/null 2>&1 - docker rm ignite-cli > /dev/null 2>&1 - docker run \ - --name ignite-cli \ - -d \ - -p 26657:26657 \ - -p 1317:1317 \ - -v $PWD:/src \ - -v $PWD/apps:/apps \ - ignite-cli /bin/bash -c "trap : TERM INT; sleep infinity & wait" \ - > /dev/null -} - -function attach { - is_running=$(docker ps -q -f status=running -f name=^/ignite-cli$) - if [[ -z $is_running ]]; then run; fi - docker exec -ti ignite-cli /bin/bash -} - -case "$1" in - # build the development container. - build) build - ;; - - # restart the development container. - reset) run - ;; - - # scratch builds the container, resets existing and attaches to it. - scratch) - build - run - attach - ;; - - # attach to development container but start if not already running. - *) attach - ;; -esac - -# in container commands: -# i install Ignite CLI -# s cd into Ignite CLI's source code -# a cd into apps dir -# c cleanup previous app installations From 9d6309e92842c3fbc21bf11bb43024c001fef686 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Thu, 2 Oct 2025 21:38:23 +0200 Subject: [PATCH 10/11] cl --- changelog.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index a66b2d4a4a..1066f29662 100644 --- a/changelog.md +++ b/changelog.md @@ -4,8 +4,9 @@ ### Changes -- [#4807](https://github.com/ignite/cli/pull/4807) Improve unconfigured path message when building a chain. - [#4805](https://github.com/ignite/cli/pull/4805) Fetch fallback buf token. +- [#4807](https://github.com/ignite/cli/pull/4807) Improve unconfigured path message when building a chain. +- [#4808](https://github.com/ignite/cli/pull/4808) Remove unused packages. ## [`v29.4.0`](https://github.com/ignite/cli/releases/tag/v29.4.0) From 61da7f52e4c81a12b15dea7e290b437bf2b46421 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Fri, 3 Oct 2025 10:37:17 +0200 Subject: [PATCH 11/11] cl --- changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/changelog.md b/changelog.md index 1066f29662..28e722c100 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,8 @@ ## Unreleased +## [`v29.4.1`](https://github.com/ignite/cli/releases/tag/v29.4.1) + ### Changes - [#4805](https://github.com/ignite/cli/pull/4805) Fetch fallback buf token.