Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

### Fixes

- [#4886](https://github.com/ignite/cli/pull/4886) Fix chain scaffolding checks.
- [#4889](https://github.com/ignite/cli/pull/4889) Plugin data race.

## [`v29.8.0`](https://github.com/ignite/cli/releases/tag/v29.8.0)
Expand Down
5 changes: 4 additions & 1 deletion ignite/services/plugin/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -425,5 +425,8 @@ func (p *Plugin) outdatedBinary() bool {
fmt.Printf("error while walking app source path %q\n", p.srcPath)
return false
}
return mostRecent.After(binaryTime)
// Rebuild when source files are newer OR have the same timestamp as the binary.
// In some environments (such as fresh CI checkouts), mtimes can be normalized
// to identical values, and strict "after" checks may incorrectly reuse stale binaries.
return !mostRecent.Before(binaryTime)
}
22 changes: 22 additions & 0 deletions ignite/services/plugin/plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,28 @@ func TestPluginClean(t *testing.T) {
}
}

func TestPluginOutdatedBinary(t *testing.T) {
t.Run("returns true when source and binary mtimes are equal", func(t *testing.T) {
tmp := t.TempDir()
srcFile := filepath.Join(tmp, "main.go")
binFile := filepath.Join(tmp, "app.ign")

require.NoError(t, os.WriteFile(srcFile, []byte("package main\n"), 0o644))
require.NoError(t, os.WriteFile(binFile, []byte("binary"), 0o755))

equalTime := time.Now().Add(-time.Minute).Truncate(time.Second)
require.NoError(t, os.Chtimes(srcFile, equalTime, equalTime))
require.NoError(t, os.Chtimes(binFile, equalTime, equalTime))

p := Plugin{
srcPath: tmp,
name: "app",
}

require.True(t, p.outdatedBinary())
})
}

// scaffoldPlugin runs Scaffold and updates the go.mod so it uses the
// current ignite/cli sources.
func scaffoldPlugin(t *testing.T, dir, name string, sharedHost bool) string {
Expand Down
29 changes: 29 additions & 0 deletions ignite/services/scaffolder/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,35 @@ func checkComponentCreated(appPath, moduleName string, compName multiformatname.
return err
}

// checkTypeProtoCreated checks if the proto type already exists in the module proto package.
func checkTypeProtoCreated(
ctx context.Context,
appPath, appName, protoDir, moduleName string,
compName multiformatname.Name,
) error {
path := filepath.Join(appPath, protoDir, appName, moduleName)
pkgs, err := protoanalysis.Parse(ctx, protoanalysis.NewCache(), path)
if err != nil {
return err
}

for _, pkg := range pkgs {
for _, msg := range pkg.Messages {
if !strings.EqualFold(msg.Name, compName.PascalCase) {
continue
}

return errors.Errorf("component %s with name %s is already created (type %s exists)",
componentType,
compName.Original,
msg.Name,
)
}
}

return nil
}

// checkCustomTypes returns error if one of the types is invalid.
func checkCustomTypes(ctx context.Context, appPath, appName, protoDir, module string, fields []string) error {
path := filepath.Join(appPath, protoDir, appName, module)
Expand Down
42 changes: 42 additions & 0 deletions ignite/services/scaffolder/component_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package scaffolder

import (
"context"
"os"
"path/filepath"
"testing"

"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -105,3 +108,42 @@ func TestContainsCustomTypes(t *testing.T) {
})
}
}

func TestCheckTypeProtoCreated(t *testing.T) {
t.Run("should fail when proto type already exists", func(t *testing.T) {
tmp := t.TempDir()
protoFile := filepath.Join(tmp, "proto", "blog", "blog", "v1", "post.proto")
require.NoError(t, os.MkdirAll(filepath.Dir(protoFile), 0o755))

content := `syntax = "proto3";
package blog.blog.v1;

message Post {}
`
require.NoError(t, os.WriteFile(protoFile, []byte(content), 0o644))

name, err := multiformatname.NewName("post")
require.NoError(t, err)

err = checkTypeProtoCreated(context.Background(), tmp, "blog", "proto", "blog", name)
require.EqualError(t, err, "component type with name post is already created (type Post exists)")
})

t.Run("should pass when proto type does not exist", func(t *testing.T) {
tmp := t.TempDir()
protoFile := filepath.Join(tmp, "proto", "blog", "blog", "v1", "comment.proto")
require.NoError(t, os.MkdirAll(filepath.Dir(protoFile), 0o755))

content := `syntax = "proto3";
package blog.blog.v1;

message Comment {}
`
require.NoError(t, os.WriteFile(protoFile, []byte(content), 0o644))

name, err := multiformatname.NewName("post")
require.NoError(t, err)

require.NoError(t, checkTypeProtoCreated(context.Background(), tmp, "blog", "proto", "blog", name))
})
}
9 changes: 8 additions & 1 deletion ignite/services/scaffolder/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,11 @@ func SingletonType() AddTypeKind {

// DryType only creates a type with a basic definition.
func DryType() AddTypeKind {
return func(*addTypeOptions) {}
return func(o *addTypeOptions) {
// Dry type scaffolding only adds a proto type definition and never generates CRUD messages.
// Force this option so component validity checks don't treat existing Msg* types as conflicts.
o.withoutMessage = true
}
}

// TypeWithModule module to scaffold type into.
Expand Down Expand Up @@ -140,6 +144,9 @@ func (s Scaffolder) AddType(
if err := checkComponentValidity(s.appPath, moduleName, name, o.withoutMessage); err != nil {
return err
}
if err := checkTypeProtoCreated(ctx, s.appPath, s.modpath.Package, s.protoDir, moduleName, name); err != nil {
return err
}

// Check and parse provided fields
if err := checkCustomTypes(ctx, s.appPath, s.modpath.Package, s.protoDir, moduleName, o.fields); err != nil {
Expand Down
12 changes: 12 additions & 0 deletions ignite/services/scaffolder/type_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,18 @@ func TestParseTypeFields(t *testing.T) {
},
},
},
{
name: "dry type defaults to no message",
addKind: DryType(),
addOptions: []AddTypeOption{},
expectedOptions: addTypeOptions{
moduleName: testModuleName,
withoutMessage: true,
signer: testSigner,
},
shouldError: false,
expectedFields: nil,
},
}

for _, tc := range tests {
Expand Down