diff --git a/cmd/odo/alizer_test.go b/cmd/odo/alizer_test.go index 304523d17..88c453653 100644 --- a/cmd/odo/alizer_test.go +++ b/cmd/odo/alizer_test.go @@ -52,14 +52,15 @@ func TestOdoAlizer(t *testing.T) { alizerClient := alizer.NewMockClient(ctrl) path := "/" alizerClient.EXPECT().DetectFramework(gomock.Any(), path). - Return( - model.DevFileType{ + Return(alizer.DetectedFramework{ + Type: model.DevFileType{ Name: "framework-name", }, - "1.1.1", - api.Registry{ + DefaultVersion: "1.1.1", + Registry: api.Registry{ Name: "TheRegistryName", }, + }, nil, ) alizerClient.EXPECT().DetectPorts(path).Return([]int{8080, 3000}, nil) @@ -92,14 +93,15 @@ func TestOdoAlizer(t *testing.T) { alizerClient := alizer.NewMockClient(ctrl) path := "/" alizerClient.EXPECT().DetectFramework(gomock.Any(), path). - Return( - model.DevFileType{ + Return(alizer.DetectedFramework{ + Type: model.DevFileType{ Name: "framework-name", }, - "1.1.1", - api.Registry{ + DefaultVersion: "1.1.1", + Registry: api.Registry{ Name: "TheRegistryName", }, + }, nil, ) alizerClient.EXPECT().DetectPorts(path).Return([]int{8080, 3000}, nil) diff --git a/docs/website/docs/command-reference/docs-mdx/init/interactive_mode_directory_with_sources_output.mdx b/docs/website/docs/command-reference/docs-mdx/init/interactive_mode_directory_with_sources_output.mdx index b47c2bdbb..0a0b40f4c 100644 --- a/docs/website/docs/command-reference/docs-mdx/init/interactive_mode_directory_with_sources_output.mdx +++ b/docs/website/docs/command-reference/docs-mdx/init/interactive_mode_directory_with_sources_output.mdx @@ -9,6 +9,7 @@ $ odo init Interactive mode enabled, please answer the following questions: ✓ Determining a Devfile for the current directory [1s] Based on the files in the current directory odo detected +Supported architectures: all Language: JavaScript Project type: Node.js Application ports: 8080 diff --git a/docs/website/docs/command-reference/docs-mdx/init/interactive_mode_empty_directory_output.mdx b/docs/website/docs/command-reference/docs-mdx/init/interactive_mode_empty_directory_output.mdx index 96aac5ce2..804622b29 100644 --- a/docs/website/docs/command-reference/docs-mdx/init/interactive_mode_empty_directory_output.mdx +++ b/docs/website/docs/command-reference/docs-mdx/init/interactive_mode_empty_directory_output.mdx @@ -7,6 +7,12 @@ $ odo init \__/ Interactive mode enabled, please answer the following questions: +? Select architectures to filter by: [Use arrows to move, space to select, to all, to none, type to filter] +> [x] amd64 +[ ] arm64 +[ ] ppc64le +[ ] s390x +? Select architectures to filter by: amd64 ? Select language: Java ? Select project type: Maven Java ✓ Downloading devfile "java-maven" from registry "DefaultDevfileRegistry" [4s] diff --git a/docs/website/docs/user-guides/quickstart/docs-mdx/dotnet/dotnet_odo_init_output.mdx b/docs/website/docs/user-guides/quickstart/docs-mdx/dotnet/dotnet_odo_init_output.mdx index 9ec792432..d122b1664 100644 --- a/docs/website/docs/user-guides/quickstart/docs-mdx/dotnet/dotnet_odo_init_output.mdx +++ b/docs/website/docs/user-guides/quickstart/docs-mdx/dotnet/dotnet_odo_init_output.mdx @@ -9,10 +9,17 @@ $ odo init Interactive mode enabled, please answer the following questions: ✓ Determining a Devfile for the current directory [1s] Based on the files in the current directory odo detected +Supported architectures: all Language: .NET Project type: dotnet The devfile "dotnet50:1.0.3" from the registry "DefaultDevfileRegistry" will be downloaded. ? Is this correct? No +? Select architectures to filter by: [Use arrows to move, space to select, to all, to none, type to filter] +> [x] amd64 +[ ] arm64 +[ ] ppc64le +[ ] s390x +? Select architectures to filter by: amd64 ? Select language: .NET ? Select project type: .NET 6.0 ✓ Downloading devfile "dotnet60" from registry "DefaultDevfileRegistry" [3s] diff --git a/docs/website/docs/user-guides/quickstart/docs-mdx/go/go_odo_init_output.mdx b/docs/website/docs/user-guides/quickstart/docs-mdx/go/go_odo_init_output.mdx index 4128aeb48..207ad33dd 100644 --- a/docs/website/docs/user-guides/quickstart/docs-mdx/go/go_odo_init_output.mdx +++ b/docs/website/docs/user-guides/quickstart/docs-mdx/go/go_odo_init_output.mdx @@ -9,6 +9,7 @@ $ odo init Interactive mode enabled, please answer the following questions: ✓ Determining a Devfile for the current directory [1s] Based on the files in the current directory odo detected +Supported architectures: all Language: Go Project type: Go Application ports: 8080 diff --git a/docs/website/docs/user-guides/quickstart/docs-mdx/java/java_odo_init_output.mdx b/docs/website/docs/user-guides/quickstart/docs-mdx/java/java_odo_init_output.mdx index 6e6c62295..13d35d0bb 100644 --- a/docs/website/docs/user-guides/quickstart/docs-mdx/java/java_odo_init_output.mdx +++ b/docs/website/docs/user-guides/quickstart/docs-mdx/java/java_odo_init_output.mdx @@ -9,6 +9,7 @@ $ odo init Interactive mode enabled, please answer the following questions: ✓ Determining a Devfile for the current directory [1s] Based on the files in the current directory odo detected +Supported architectures: all Language: Java Project type: springboot The devfile "java-springboot:1.2.0" from the registry "DefaultDevfileRegistry" will be downloaded. diff --git a/docs/website/docs/user-guides/quickstart/docs-mdx/nodejs/nodejs_odo_init_output.mdx b/docs/website/docs/user-guides/quickstart/docs-mdx/nodejs/nodejs_odo_init_output.mdx index ee952c0cd..e342a84e0 100644 --- a/docs/website/docs/user-guides/quickstart/docs-mdx/nodejs/nodejs_odo_init_output.mdx +++ b/docs/website/docs/user-guides/quickstart/docs-mdx/nodejs/nodejs_odo_init_output.mdx @@ -9,6 +9,7 @@ $ odo init Interactive mode enabled, please answer the following questions: ✓ Determining a Devfile for the current directory [1s] Based on the files in the current directory odo detected +Supported architectures: all Language: JavaScript Project type: Node.js Application ports: 3000 diff --git a/pkg/alizer/alizer.go b/pkg/alizer/alizer.go index af9f0740a..1d3c82ce9 100644 --- a/pkg/alizer/alizer.go +++ b/pkg/alizer/alizer.go @@ -29,11 +29,11 @@ func NewAlizerClient(registryClient registry.Client) *Alizer { // DetectFramework uses the alizer library in order to detect the devfile // to use depending on the files in the path -func (o *Alizer) DetectFramework(ctx context.Context, path string) (_ model.DevFileType, defaultVersion string, _ api.Registry, _ error) { +func (o *Alizer) DetectFramework(ctx context.Context, path string) (DetectedFramework, error) { types := []model.DevFileType{} components, err := o.registryClient.ListDevfileStacks(ctx, "", "", "", false, false) if err != nil { - return model.DevFileType{}, defaultVersion, api.Registry{}, err + return DetectedFramework{}, err } for _, component := range components.Items { types = append(types, model.DevFileType{ @@ -45,15 +45,21 @@ func (o *Alizer) DetectFramework(ctx context.Context, path string) (_ model.DevF } typ, err := recognizer.SelectDevFileFromTypes(path, types) if err != nil { - return model.DevFileType{}, defaultVersion, api.Registry{}, err + return DetectedFramework{}, err } // Get the default stack version that will be downloaded + var defaultVersion string for _, version := range components.Items[typ].Versions { if version.IsDefault { defaultVersion = version.Version } } - return types[typ], defaultVersion, components.Items[typ].Registry, nil + return DetectedFramework{ + Type: types[typ], + DefaultVersion: defaultVersion, + Registry: components.Items[typ].Registry, + Architectures: components.Items[typ].Architectures, + }, nil } // DetectName retrieves the name of the project (if available). diff --git a/pkg/alizer/alizer_test.go b/pkg/alizer/alizer_test.go index f5723408f..14cd92b40 100644 --- a/pkg/alizer/alizer_test.go +++ b/pkg/alizer/alizer_test.go @@ -119,18 +119,18 @@ func TestDetectFramework(t *testing.T) { registryClient.EXPECT().ListDevfileStacks(ctx, "", "", "", false, false).Return(list, nil) alizerClient := NewAlizerClient(registryClient) // Run function DetectFramework - detected, _, registry, err := alizerClient.DetectFramework(ctx, tt.args.path) + detected, err := alizerClient.DetectFramework(ctx, tt.args.path) if !tt.wantErr == (err != nil) { t.Errorf("unexpected error %v, wantErr %v", err, tt.wantErr) return } - if detected.Name != tt.wantedDevfile { + if detected.Type.Name != tt.wantedDevfile { t.Errorf("unexpected devfile %v, wantedDevfile %v", detected, tt.wantedDevfile) } - if registry.Name != tt.wantedRegistry { - t.Errorf("unexpected registry %v, wantedRegistry %v", registry, tt.wantedRegistry) + if detected.Registry.Name != tt.wantedRegistry { + t.Errorf("unexpected registry %v, wantedRegistry %v", detected.Registry, tt.wantedRegistry) } }) } diff --git a/pkg/alizer/interface.go b/pkg/alizer/interface.go index 78256dbb4..c857baba8 100644 --- a/pkg/alizer/interface.go +++ b/pkg/alizer/interface.go @@ -4,12 +4,18 @@ import ( "context" "github.com/devfile/alizer/pkg/apis/model" - "github.com/redhat-developer/odo/pkg/api" ) +type DetectedFramework struct { + Type model.DevFileType + DefaultVersion string + Registry api.Registry + Architectures []string +} + type Client interface { - DetectFramework(ctx context.Context, path string) (_ model.DevFileType, defaultVersion string, _ api.Registry, _ error) + DetectFramework(ctx context.Context, path string) (DetectedFramework, error) DetectName(path string) (string, error) DetectPorts(path string) ([]int, error) } diff --git a/pkg/alizer/mock.go b/pkg/alizer/mock.go index e766fd725..383a2894f 100644 --- a/pkg/alizer/mock.go +++ b/pkg/alizer/mock.go @@ -8,9 +8,7 @@ import ( context "context" reflect "reflect" - model "github.com/devfile/alizer/pkg/apis/model" gomock "github.com/golang/mock/gomock" - api "github.com/redhat-developer/odo/pkg/api" ) // MockClient is a mock of Client interface. @@ -37,14 +35,12 @@ func (m *MockClient) EXPECT() *MockClientMockRecorder { } // DetectFramework mocks base method. -func (m *MockClient) DetectFramework(ctx context.Context, path string) (model.DevFileType, string, api.Registry, error) { +func (m *MockClient) DetectFramework(ctx context.Context, path string) (DetectedFramework, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DetectFramework", ctx, path) - ret0, _ := ret[0].(model.DevFileType) - ret1, _ := ret[1].(string) - ret2, _ := ret[2].(api.Registry) - ret3, _ := ret[3].(error) - return ret0, ret1, ret2, ret3 + ret0, _ := ret[0].(DetectedFramework) + ret1, _ := ret[1].(error) + return ret0, ret1 } // DetectFramework indicates an expected call of DetectFramework. diff --git a/pkg/api/analyze.go b/pkg/api/analyze.go index b85f343b0..0273c62ad 100644 --- a/pkg/api/analyze.go +++ b/pkg/api/analyze.go @@ -19,4 +19,6 @@ type DetectionResult struct { DevfileVersion string `json:"devfileVersion,omitempty"` // Name represents the project/application name as detected by alizer Name string `json:"name,omitempty"` + // Architectures represent the architectures with which the Devfile must be compatible with. + Architectures []string `json:"architectures,omitempty"` } diff --git a/pkg/init/asker/asker.go b/pkg/init/asker/asker.go index 143a67642..90f458b08 100644 --- a/pkg/init/asker/asker.go +++ b/pkg/init/asker/asker.go @@ -24,18 +24,36 @@ func NewSurveyAsker() *Survey { return &Survey{} } -func (o *Survey) AskLanguage(langs []string) (string, error) { +func (o *Survey) AskArchitectures(archs []string, selectedDefault []string) ([]string, error) { + question := &survey.MultiSelect{ + Message: "Select architectures to filter by:", + Options: archs, + Default: selectedDefault, + } + var answer []string + err := survey.AskOne(question, &answer) + if err != nil { + return nil, err + } + return answer, nil +} + +func (o *Survey) AskLanguage(langs []string) (bool, string, error) { sort.Strings(langs) + langs = append(langs, GOBACK) question := &survey.Select{ Message: "Select language:", Options: langs, } - var answer string - err := survey.AskOne(question, &answer) + var answerPos int + err := survey.AskOne(question, &answerPos) if err != nil { - return "", err + return false, "", err } - return answer, nil + if answerPos == len(langs)-1 { + return true, "", nil + } + return false, langs[answerPos], nil } func (o *Survey) AskType(types registry.TypesWithDetails) (back bool, _ api.DevfileStack, _ error) { diff --git a/pkg/init/asker/interface.go b/pkg/init/asker/interface.go index 28713143f..30f0c5fcf 100644 --- a/pkg/init/asker/interface.go +++ b/pkg/init/asker/interface.go @@ -9,8 +9,12 @@ import ( // Asker interactively asks for information to the user type Asker interface { - // AskLanguage asks for a language, from a list of language names. The language name is returned - AskLanguage(langs []string) (string, error) + // AskArchitectures asks for a selection of architectures from a list of architecture names + AskArchitectures(archs []string, selectedDefault []string) ([]string, error) + + // AskLanguage asks for a language, from a list of language names. + // back is returned as true if the user selected to go back, or the language name is returned + AskLanguage(langs []string) (back bool, result string, err error) // AskType asks for a Devfile type, or to go back. back is returned as true if the user selected to go back, // or the selected type is returned diff --git a/pkg/init/asker/mock.go b/pkg/init/asker/mock.go index 7ad0f2ee3..af5ced6aa 100644 --- a/pkg/init/asker/mock.go +++ b/pkg/init/asker/mock.go @@ -66,6 +66,21 @@ func (mr *MockAskerMockRecorder) AskAddPort() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AskAddPort", reflect.TypeOf((*MockAsker)(nil).AskAddPort)) } +// AskArchitectures mocks base method. +func (m *MockAsker) AskArchitectures(archs, selectedDefault []string) ([]string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AskArchitectures", archs, selectedDefault) + ret0, _ := ret[0].([]string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AskArchitectures indicates an expected call of AskArchitectures. +func (mr *MockAskerMockRecorder) AskArchitectures(archs, selectedDefault interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AskArchitectures", reflect.TypeOf((*MockAsker)(nil).AskArchitectures), archs, selectedDefault) +} + // AskContainerName mocks base method. func (m *MockAsker) AskContainerName(containers []string) (string, error) { m.ctrl.T.Helper() @@ -97,12 +112,13 @@ func (mr *MockAskerMockRecorder) AskCorrect() *gomock.Call { } // AskLanguage mocks base method. -func (m *MockAsker) AskLanguage(langs []string) (string, error) { +func (m *MockAsker) AskLanguage(langs []string) (bool, string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AskLanguage", langs) - ret0, _ := ret[0].(string) - ret1, _ := ret[1].(error) - return ret0, ret1 + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(string) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 } // AskLanguage indicates an expected call of AskLanguage. diff --git a/pkg/init/backend/alizer.go b/pkg/init/backend/alizer.go index 5552bb934..d9861c530 100644 --- a/pkg/init/backend/alizer.go +++ b/pkg/init/backend/alizer.go @@ -3,10 +3,11 @@ package backend import ( "context" "fmt" - "github.com/redhat-developer/odo/pkg/log" "strconv" "strings" + "github.com/redhat-developer/odo/pkg/log" + "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" "github.com/devfile/library/v2/pkg/devfile/parser" @@ -34,6 +35,13 @@ func (o *AlizerBackend) Validate(flags map[string]string, fs filesystem.Filesyst return nil } +func archList(archs []string) string { + if len(archs) == 0 { + return "all" + } + return strings.Join(archs, ", ") +} + // SelectDevfile calls the Alizer to detect the devfile and asks for confirmation to the user func (o *AlizerBackend) SelectDevfile(ctx context.Context, flags map[string]string, fs filesystem.Filesystem, dir string) (*api.DetectionResult, error) { type result struct { @@ -47,12 +55,13 @@ func (o *AlizerBackend) SelectDevfile(ctx context.Context, flags map[string]stri location, err := func() (location *api.DetectionResult, err error) { spinner := log.Spinnerf("Determining a Devfile for the current directory") defer spinner.End(err == nil) - selected, defaultVersion, registry, err := o.alizerClient.DetectFramework(ctx, dir) + detected, err := o.alizerClient.DetectFramework(ctx, dir) if err != nil { return nil, err } - msg := fmt.Sprintf("Based on the files in the current directory odo detected\nLanguage: %s\nProject type: %s", selected.Language, selected.ProjectType) + msg := fmt.Sprintf("Based on the files in the current directory odo detected\nSupported architectures: %s\nLanguage: %s\nProject type: %s", + archList(detected.Architectures), detected.Type.Language, detected.Type.ProjectType) appPorts, err := o.alizerClient.DetectPorts(dir) if err != nil { @@ -68,7 +77,7 @@ func (o *AlizerBackend) SelectDevfile(ctx context.Context, flags map[string]stri } fmt.Println(msg) - fmt.Printf("The devfile \"%s:%s\" from the registry %q will be downloaded.\n", selected.Name, defaultVersion, registry.Name) + fmt.Printf("The devfile \"%s:%s\" from the registry %q will be downloaded.\n", detected.Type.Name, detected.DefaultVersion, detected.Registry.Name) confirm, err := o.askerClient.AskCorrect() if err != nil { return nil, err @@ -76,7 +85,7 @@ func (o *AlizerBackend) SelectDevfile(ctx context.Context, flags map[string]stri if !confirm { return nil, nil } - return alizer.NewDetectionResult(selected, registry, appPorts, defaultVersion, ""), nil + return alizer.NewDetectionResult(detected.Type, detected.Registry, appPorts, detected.DefaultVersion, ""), nil }() resultChan <- result{ location: location, diff --git a/pkg/init/backend/alizer_test.go b/pkg/init/backend/alizer_test.go index 3cb09e138..cb7c41478 100644 --- a/pkg/init/backend/alizer_test.go +++ b/pkg/init/backend/alizer_test.go @@ -53,11 +53,7 @@ func TestAlizerBackend_SelectDevfile(t *testing.T) { }, alizerClient: func(ctrl *gomock.Controller) alizer.Client { alizerClient := alizer.NewMockClient(ctrl) - alizerClient.EXPECT().DetectFramework(gomock.Any(), gomock.Any()).Return(model.DevFileType{ - Name: "a-devfile-name", - }, "1.0.0", api.Registry{ - Name: "a-registry", - }, errors.New("unable to detect framework")) + alizerClient.EXPECT().DetectFramework(gomock.Any(), gomock.Any()).Return(alizer.DetectedFramework{}, errors.New("unable to detect framework")) return alizerClient }, }, @@ -77,11 +73,7 @@ func TestAlizerBackend_SelectDevfile(t *testing.T) { }, alizerClient: func(ctrl *gomock.Controller) alizer.Client { alizerClient := alizer.NewMockClient(ctrl) - alizerClient.EXPECT().DetectFramework(gomock.Any(), gomock.Any()).Return(model.DevFileType{ - Name: "a-devfile-name", - }, "1.0.0", api.Registry{ - Name: "a-registry", - }, nil) + alizerClient.EXPECT().DetectFramework(gomock.Any(), gomock.Any()).Return(alizer.DetectedFramework{}, nil) alizerClient.EXPECT().DetectPorts(gomock.Any()).Return(nil, errors.New("unable to detect ports")) return alizerClient }, @@ -102,10 +94,14 @@ func TestAlizerBackend_SelectDevfile(t *testing.T) { }, alizerClient: func(ctrl *gomock.Controller) alizer.Client { alizerClient := alizer.NewMockClient(ctrl) - alizerClient.EXPECT().DetectFramework(gomock.Any(), gomock.Any()).Return(model.DevFileType{ - Name: "a-devfile-name", - }, "1.0.0", api.Registry{ - Name: "a-registry", + alizerClient.EXPECT().DetectFramework(gomock.Any(), gomock.Any()).Return(alizer.DetectedFramework{ + Type: model.DevFileType{ + Name: "a-devfile-name", + }, + DefaultVersion: "1.0.0", + Registry: api.Registry{ + Name: "a-registry", + }, }, nil) alizerClient.EXPECT().DetectPorts(gomock.Any()).Return(nil, nil) return alizerClient @@ -127,10 +123,14 @@ func TestAlizerBackend_SelectDevfile(t *testing.T) { }, alizerClient: func(ctrl *gomock.Controller) alizer.Client { alizerClient := alizer.NewMockClient(ctrl) - alizerClient.EXPECT().DetectFramework(gomock.Any(), gomock.Any()).Return(model.DevFileType{ - Name: "a-devfile-name", - }, "1.0.0", api.Registry{ - Name: "a-registry", + alizerClient.EXPECT().DetectFramework(gomock.Any(), gomock.Any()).Return(alizer.DetectedFramework{ + Type: model.DevFileType{ + Name: "a-devfile-name", + }, + DefaultVersion: "1.0.0", + Registry: api.Registry{ + Name: "a-registry", + }, }, nil) alizerClient.EXPECT().DetectPorts(gomock.Any()).Return(nil, nil) return alizerClient @@ -156,7 +156,7 @@ func TestAlizerBackend_SelectDevfile(t *testing.T) { }, alizerClient: func(ctrl *gomock.Controller) alizer.Client { alizerClient := alizer.NewMockClient(ctrl) - alizerClient.EXPECT().DetectFramework(gomock.Any(), gomock.Any()).Return(model.DevFileType{}, "", api.Registry{}, nil) + alizerClient.EXPECT().DetectFramework(gomock.Any(), gomock.Any()).Return(alizer.DetectedFramework{}, nil) alizerClient.EXPECT().DetectPorts(gomock.Any()).Return(nil, nil) return alizerClient }, @@ -177,10 +177,14 @@ func TestAlizerBackend_SelectDevfile(t *testing.T) { }, alizerClient: func(ctrl *gomock.Controller) alizer.Client { alizerClient := alizer.NewMockClient(ctrl) - alizerClient.EXPECT().DetectFramework(gomock.Any(), gomock.Any()).Return(model.DevFileType{ - Name: "a-devfile-name", - }, "1.0.0", api.Registry{ - Name: "a-registry", + alizerClient.EXPECT().DetectFramework(gomock.Any(), gomock.Any()).Return(alizer.DetectedFramework{ + Type: model.DevFileType{ + Name: "a-devfile-name", + }, + DefaultVersion: "1.0.0", + Registry: api.Registry{ + Name: "a-registry", + }, }, nil) alizerClient.EXPECT().DetectPorts(gomock.Any()).Return([]int{1234, 5678}, nil) return alizerClient diff --git a/pkg/init/backend/flags.go b/pkg/init/backend/flags.go index d6217e6b2..fa4a807ba 100644 --- a/pkg/init/backend/flags.go +++ b/pkg/init/backend/flags.go @@ -13,6 +13,7 @@ import ( "github.com/redhat-developer/odo/pkg/registry" "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" + "github.com/devfile/api/v2/pkg/devfile" "github.com/devfile/library/v2/pkg/devfile/parser" "github.com/devfile/library/v2/pkg/devfile/parser/data/v2/common" dfutil "github.com/devfile/library/v2/pkg/util" @@ -30,6 +31,7 @@ const ( FLAG_DEVFILE_PATH = "devfile-path" FLAG_DEVFILE_VERSION = "devfile-version" FLAG_RUN_PORT = "run-port" + FLAG_ARCHITECTURE = "architecture" ) // FlagsBackend is a backend that will extract all needed information from flags passed to the command @@ -39,6 +41,13 @@ type FlagsBackend struct { var _ InitBackend = (*FlagsBackend)(nil) +var knownArchitectures []string = []string{ + string(devfile.AMD64), + string(devfile.ARM64), + string(devfile.PPC64LE), + string(devfile.S390X), +} + func NewFlagsBackend(registryClient registry.Client) *FlagsBackend { return &FlagsBackend{ registryClient: registryClient, @@ -97,15 +106,37 @@ Please use 'odo preference registry'' command to configure devfile return errors.New("--starter parameter cannot be used when the directory is not empty") } + archs, err := parseStringArrayFlagValue(flags[FLAG_ARCHITECTURE]) + if err != nil { + return err + } + for _, arch := range archs { + if !isKnownArch(arch) { + return fmt.Errorf("value %q is not valid for flag --architecture. Possible values are: %s", arch, strings.Join(knownArchitectures, ", ")) + } + } + return nil } +func isKnownArch(arch string) bool { + for _, known := range knownArchitectures { + if known == arch { + return true + } + } + return false +} + func (o *FlagsBackend) SelectDevfile(ctx context.Context, flags map[string]string, _ filesystem.Filesystem, _ string) (*api.DetectionResult, error) { + // This has been validated before + archs, _ := parseStringArrayFlagValue(flags[FLAG_ARCHITECTURE]) return &api.DetectionResult{ Devfile: flags[FLAG_DEVFILE], DevfileRegistry: flags[FLAG_DEVFILE_REGISTRY], DevfilePath: flags[FLAG_DEVFILE_PATH], DevfileVersion: flags[FLAG_DEVFILE_VERSION], + Architectures: archs, }, nil } @@ -150,16 +181,16 @@ func (o FlagsBackend) HandleApplicationPorts(devfileobj parser.DevfileObj, _ []i func setPortsForFlag(devfileobj parser.DevfileObj, flags map[string]string, flagName string) (parser.DevfileObj, error) { flagVal := flags[flagName] - // Repeatable flags are formatted as "[val1,val2]" - if !(strings.HasPrefix(flagVal, "[") && strings.HasSuffix(flagVal, "]")) { + + split, err := parseStringArrayFlagValue(flagVal) + if err != nil || len(split) == 0 { return devfileobj, nil } - portsStr := flagVal[1 : len(flagVal)-1] var ports []int - split := strings.Split(portsStr, ",") for _, s := range split { - p, err := strconv.Atoi(s) + var p int + p, err = strconv.Atoi(s) if err != nil { return parser.DevfileObj{}, fmt.Errorf("invalid value for %s (%q): %w", flagName, s, err) } @@ -216,3 +247,15 @@ func setPortsForFlag(devfileobj parser.DevfileObj, flags map[string]string, flag } return devfileobj, nil } + +func parseStringArrayFlagValue(flagVal string) ([]string, error) { + if flagVal == "" { + return []string{}, nil + } + // Repeatable flags are formatted as "[val1,val2]" + if !(strings.HasPrefix(flagVal, "[") && strings.HasSuffix(flagVal, "]")) { + return nil, fmt.Errorf("malformed value %q", flagVal) + } + portsStr := flagVal[1 : len(flagVal)-1] + return strings.Split(portsStr, ","), nil +} diff --git a/pkg/init/backend/flags_test.go b/pkg/init/backend/flags_test.go index 16a7a2660..09a2448db 100644 --- a/pkg/init/backend/flags_test.go +++ b/pkg/init/backend/flags_test.go @@ -45,6 +45,7 @@ func TestFlagsBackend_SelectDevfile(t *testing.T) { Devfile: "adevfile", DevfilePath: "apath", DevfileRegistry: "aregistry", + Architectures: []string{}, }, }, } diff --git a/pkg/init/backend/interactive.go b/pkg/init/backend/interactive.go index ab592d1be..d537bf6ef 100644 --- a/pkg/init/backend/interactive.go +++ b/pkg/init/backend/interactive.go @@ -6,6 +6,7 @@ import ( "path/filepath" "sort" "strconv" + "strings" "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" "github.com/devfile/library/v2/pkg/devfile/parser" @@ -22,7 +23,8 @@ import ( ) const ( - STATE_ASK_LANG = iota + STATE_ASK_ARCHITECTURES = iota + STATE_ASK_LANG STATE_ASK_TYPE STATE_ASK_VERSION STATE_END @@ -51,22 +53,36 @@ func (o *InteractiveBackend) Validate(flags map[string]string, fs filesystem.Fil func (o *InteractiveBackend) SelectDevfile(ctx context.Context, flags map[string]string, _ filesystem.Filesystem, _ string) (*api.DetectionResult, error) { result := &api.DetectionResult{} - devfileEntries, _ := o.registryClient.ListDevfileStacks(ctx, "", "", "", false, false) - - langs := devfileEntries.GetLanguages() - state := STATE_ASK_LANG + var devfileEntries registry.DevfileStackList + state := STATE_ASK_ARCHITECTURES var lang string + archs := []string{"amd64"} var err error var details api.DevfileStack loop: for { switch state { - case STATE_ASK_LANG: - lang, err = o.askerClient.AskLanguage(langs) + case STATE_ASK_ARCHITECTURES: + archs, err = o.askerClient.AskArchitectures(knownArchitectures, archs) if err != nil { return nil, err } + state = STATE_ASK_LANG + + case STATE_ASK_LANG: + filter := strings.Join(archs, ",") + devfileEntries, _ = o.registryClient.ListDevfileStacks(ctx, "", "", filter, false, false) + langs := devfileEntries.GetLanguages() + var back bool + back, lang, err = o.askerClient.AskLanguage(langs) + if err != nil { + return nil, err + } + if back { + state = STATE_ASK_ARCHITECTURES + continue loop + } state = STATE_ASK_TYPE case STATE_ASK_TYPE: diff --git a/pkg/init/backend/interactive_test.go b/pkg/init/backend/interactive_test.go index 76e6da862..4d1e416c6 100644 --- a/pkg/init/backend/interactive_test.go +++ b/pkg/init/backend/interactive_test.go @@ -36,7 +36,8 @@ func TestInteractiveBackend_SelectDevfile(t *testing.T) { fields: fields{ buildAsker: func(ctrl *gomock.Controller) asker.Asker { client := asker.NewMockAsker(ctrl) - client.EXPECT().AskLanguage(gomock.Any()).Return("java", nil) + client.EXPECT().AskArchitectures(knownArchitectures, []string{"amd64"}).Return([]string{"amd64"}, nil) + client.EXPECT().AskLanguage(gomock.Any()).Return(false, "java", nil) client.EXPECT().AskType(gomock.Any()).Return(false, api.DevfileStack{ Name: "a-devfile-name", Registry: api.Registry{ @@ -57,13 +58,14 @@ func TestInteractiveBackend_SelectDevfile(t *testing.T) { }, }, { - name: "selection with back", + name: "selection with back on language selection", fields: fields{ buildAsker: func(ctrl *gomock.Controller) asker.Asker { client := asker.NewMockAsker(ctrl) - client.EXPECT().AskLanguage(gomock.Any()).Return("java", nil) - client.EXPECT().AskType(gomock.Any()).Return(true, api.DevfileStack{}, nil) - client.EXPECT().AskLanguage(gomock.Any()).Return("go", nil) + client.EXPECT().AskArchitectures(knownArchitectures, []string{"amd64"}).Return([]string{"amd64", "arm64"}, nil) + client.EXPECT().AskLanguage(gomock.Any()).Return(true, "", nil) + client.EXPECT().AskArchitectures(knownArchitectures, []string{"amd64", "arm64"}).Return([]string{"arm64"}, nil) + client.EXPECT().AskLanguage(gomock.Any()).Return(false, "go", nil) client.EXPECT().AskType(gomock.Any()).Return(false, api.DevfileStack{ Name: "a-devfile-name", Registry: api.Registry{ @@ -74,7 +76,35 @@ func TestInteractiveBackend_SelectDevfile(t *testing.T) { }, buildCatalogClient: func(ctrl *gomock.Controller) registry.Client { client := registry.NewMockClient(ctrl) - client.EXPECT().ListDevfileStacks(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) + client.EXPECT().ListDevfileStacks(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(2) + return client + }, + }, + want: &api.DetectionResult{ + Devfile: "a-devfile-name", + DevfileRegistry: "MyRegistry1", + }, + }, + { + name: "selection with back on type selection", + fields: fields{ + buildAsker: func(ctrl *gomock.Controller) asker.Asker { + client := asker.NewMockAsker(ctrl) + client.EXPECT().AskArchitectures(knownArchitectures, []string{"amd64"}).Return([]string{"amd64"}, nil) + client.EXPECT().AskLanguage(gomock.Any()).Return(false, "java", nil) + client.EXPECT().AskType(gomock.Any()).Return(true, api.DevfileStack{}, nil) + client.EXPECT().AskLanguage(gomock.Any()).Return(false, "go", nil) + client.EXPECT().AskType(gomock.Any()).Return(false, api.DevfileStack{ + Name: "a-devfile-name", + Registry: api.Registry{ + Name: "MyRegistry1", + }, + }, nil) + return client + }, + buildCatalogClient: func(ctrl *gomock.Controller) registry.Client { + client := registry.NewMockClient(ctrl) + client.EXPECT().ListDevfileStacks(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(2) return client }, }, diff --git a/pkg/init/init.go b/pkg/init/init.go index 723071ad1..d05e7274d 100644 --- a/pkg/init/init.go +++ b/pkg/init/init.go @@ -50,6 +50,7 @@ var _initFlags = []string{ backend.FLAG_DEVFILE_PATH, backend.FLAG_DEVFILE_VERSION, backend.FLAG_RUN_PORT, + backend.FLAG_ARCHITECTURE, } func NewInitClient(fsys filesystem.Filesystem, preferenceClient preference.Client, registryClient registry.Client, alizerClient alizer.Client) *InitClient { @@ -137,7 +138,7 @@ func (o *InitClient) DownloadDevfile(ctx context.Context, devfileLocation *api.D if devfileLocation.DevfileVersion != "" { devfile = fmt.Sprintf("%s:%s", devfileLocation.Devfile, devfileLocation.DevfileVersion) } - return destDevfile, o.downloadFromRegistry(ctx, devfileLocation.DevfileRegistry, devfile, destDir) + return destDevfile, o.downloadFromRegistry(ctx, devfileLocation.DevfileRegistry, devfile, destDir, devfileLocation.Architectures) } } @@ -185,10 +186,14 @@ func (o *InitClient) downloadDirect(URL string, dest string) error { // downloadFromRegistry downloads a devfile from the provided registry and saves it in dest // If registryName is empty, will try to download the devfile from the list of registries in preferences -func (o *InitClient) downloadFromRegistry(ctx context.Context, registryName string, devfile string, dest string) error { +// The architectures value indicates to download a Devfile compatible with all of these architectures +func (o *InitClient) downloadFromRegistry(ctx context.Context, registryName string, devfile string, dest string, architectures []string) error { // setting NewIndexSchema ensures that the Devfile library pulls registry based on the stack version registryOptions := segment.GetRegistryOptions(ctx) registryOptions.NewIndexSchema = true + if len(architectures) > 0 { + registryOptions.Filter.Architectures = architectures + } var downloadSpinner *log.Status var forceRegistry bool diff --git a/pkg/init/init_test.go b/pkg/init/init_test.go index 986ae3ddf..75bd21362 100644 --- a/pkg/init/init_test.go +++ b/pkg/init/init_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" + "github.com/devfile/registry-support/registry-library/library" "github.com/golang/mock/gomock" "github.com/redhat-developer/odo/pkg/api" @@ -25,6 +26,7 @@ func TestInitClient_downloadFromRegistry(t *testing.T) { registryName string devfile string dest string + archs []string } tests := []struct { name string @@ -52,7 +54,12 @@ func TestInitClient_downloadFromRegistry(t *testing.T) { }, } client.EXPECT().GetDevfileRegistries(gomock.Eq("Registry1")).Return(registryList, nil).Times(1) - client.EXPECT().PullStackFromRegistry("http://registry1", "java", gomock.Any(), gomock.Any()).Return(nil).Times(1) + client.EXPECT().PullStackFromRegistry("http://registry1", "java", gomock.Any(), library.RegistryOptions{ + Telemetry: library.TelemetryData{ + Client: "odo", + }, + NewIndexSchema: true, + }).Return(nil).Times(1) return client }, }, @@ -63,6 +70,46 @@ func TestInitClient_downloadFromRegistry(t *testing.T) { }, wantErr: false, }, + { + name: "Download devfile from one specific Registry where devfile is present and arch is passed", + fields: fields{ + preferenceClient: func(ctrl *gomock.Controller) preference.Client { + client := preference.NewMockClient(ctrl) + return client + }, + registryClient: func(ctrl *gomock.Controller) registry.Client { + client := registry.NewMockClient(ctrl) + registryList := []api.Registry{ + { + Name: "Registry0", + URL: "http://registry0", + }, + { + Name: "Registry1", + URL: "http://registry1", + }, + } + client.EXPECT().GetDevfileRegistries(gomock.Eq("Registry1")).Return(registryList, nil).Times(1) + client.EXPECT().PullStackFromRegistry("http://registry1", "java", gomock.Any(), library.RegistryOptions{ + Telemetry: library.TelemetryData{ + Client: "odo", + }, + Filter: library.RegistryFilter{ + Architectures: []string{"arm64"}, + }, + NewIndexSchema: true, + }).Return(nil).Times(1) + return client + }, + }, + args: args{ + registryName: "Registry1", + devfile: "java", + dest: ".", + archs: []string{"arm64"}, + }, + wantErr: false, + }, { name: "Fail to download devfile from one specific Registry where devfile is absent", fields: fields{ @@ -167,7 +214,7 @@ func TestInitClient_downloadFromRegistry(t *testing.T) { } ctx := context.Background() ctx = envcontext.WithEnvConfig(ctx, config.Configuration{}) - if err := o.downloadFromRegistry(ctx, tt.args.registryName, tt.args.devfile, tt.args.dest); (err != nil) != tt.wantErr { + if err := o.downloadFromRegistry(ctx, tt.args.registryName, tt.args.devfile, tt.args.dest, tt.args.archs); (err != nil) != tt.wantErr { t.Errorf("InitClient.downloadFromRegistry() error = %v, wantErr %v", err, tt.wantErr) } }) diff --git a/pkg/odo/cli/alizer/alizer.go b/pkg/odo/cli/alizer/alizer.go index b8230a711..4b1d9dc86 100644 --- a/pkg/odo/cli/alizer/alizer.go +++ b/pkg/odo/cli/alizer/alizer.go @@ -50,7 +50,7 @@ func (o *AlizerOptions) Run(ctx context.Context) (err error) { // RunForJsonOutput contains the logic for the odo command func (o *AlizerOptions) RunForJsonOutput(ctx context.Context) (out interface{}, err error) { workingDir := odocontext.GetWorkingDirectory(ctx) - df, defaultVersion, reg, err := o.clientset.AlizerClient.DetectFramework(ctx, workingDir) + detected, err := o.clientset.AlizerClient.DetectFramework(ctx, workingDir) if err != nil { return nil, err } @@ -62,7 +62,7 @@ func (o *AlizerOptions) RunForJsonOutput(ctx context.Context) (out interface{}, if err != nil { return nil, err } - result := alizer.NewDetectionResult(df, reg, appPorts, defaultVersion, name) + result := alizer.NewDetectionResult(detected.Type, detected.Registry, appPorts, detected.DefaultVersion, name) return []api.DetectionResult{*result}, nil } diff --git a/pkg/odo/cli/init/init.go b/pkg/odo/cli/init/init.go index edb89f966..84868a815 100644 --- a/pkg/odo/cli/init/init.go +++ b/pkg/odo/cli/init/init.go @@ -63,6 +63,9 @@ var initExample = templates.Examples(` # Bootstrap a new component and download a starter project %[1]s --name my-app --devfile nodejs --starter nodejs-starter + + # Bootstrap a new component with a specific devfile from registry for a specific architecture + %[1]s --name my-app --devfile nodejs --architecture s390x `) type InitOptions struct { @@ -285,6 +288,7 @@ func NewCmdInit(name, fullName string, testClientset clientset.Clientset) *cobra initCmd.Flags().String(backend.FLAG_STARTER, "", "name of the starter project") initCmd.Flags().String(backend.FLAG_DEVFILE_PATH, "", "path to a devfile. This is an alternative to using devfile from Devfile registry. It can be local filesystem path or http(s) URL") initCmd.Flags().String(backend.FLAG_DEVFILE_VERSION, "", "version of the devfile stack; use \"latest\" to dowload the latest stack") + initCmd.Flags().StringArray(backend.FLAG_ARCHITECTURE, []string{}, "Architecture supported. Can be one or multiple values from amd64, arm64, ppc64le, s390x. Default is amd64.") initCmd.Flags().StringArray(backend.FLAG_RUN_PORT, []string{}, "ports used by the application (via the 'run' command)") commonflags.UseOutputFlag(initCmd) diff --git a/pkg/odo/cli/registry/registry.go b/pkg/odo/cli/registry/registry.go index 8f37cb6d6..160031069 100644 --- a/pkg/odo/cli/registry/registry.go +++ b/pkg/odo/cli/registry/registry.go @@ -32,8 +32,8 @@ var Example = ` # Get all devfile components # Filter by name and devfile registry %[1]s --filter nodejs --devfile-registry DefaultDevfileRegistry -# Filter by architecture -%[1]s --filter amd64 +# Show the Devfiles supporting both architectures +%[1]s --filter amd64,arm64 # Show more details from a specific devfile %[1]s --details --devfile nodejs @@ -120,7 +120,7 @@ func NewCmdRegistry(name, fullName string, testClientset clientset.Clientset) *c clientset.Add(listCmd, clientset.REGISTRY) // Flags - listCmd.Flags().StringVar(&o.filterFlag, "filter", "", "Filter based on the name or description or supported architecture of the component") + listCmd.Flags().StringVar(&o.filterFlag, "filter", "", "Comma-separated list of terms for filtering. Search is done using a logical AND against the name or description or supported architectures of the component.") listCmd.Flags().StringVar(&o.devfileFlag, "devfile", "", "Only the specific Devfile component") listCmd.Flags().StringVar(&o.registryFlag, "devfile-registry", "", "Only show components from the specific Devfile registry") listCmd.Flags().BoolVar(&o.detailsFlag, "details", false, "Show details of a Devfile, to be used only with --devfile") diff --git a/pkg/registry/registry.go b/pkg/registry/registry.go index e385ce803..04cb83379 100644 --- a/pkg/registry/registry.go +++ b/pkg/registry/registry.go @@ -283,32 +283,37 @@ func (o RegistryClient) ListDevfileStacks(ctx context.Context, registryName, dev devfiles := []api.DevfileStack{} + devfileLoop: for _, devfile := range registryDevfiles { // Add the "priority" of the registry to the devfile devfile.Registry.Priority = priorityNumber if filterFlag != "" { - archs := append(make([]string, 0, len(devfile.Architectures)), devfile.Architectures...) - if len(archs) == 0 { - // Devfiles with no architectures are compatible with all architectures. - archs = append(archs, - string(apidevfile.AMD64), - string(apidevfile.ARM64), - string(apidevfile.PPC64LE), - string(apidevfile.S390X), - ) - } - containsArch := func(s string) bool { - for _, arch := range archs { - if strings.Contains(arch, s) { - return true - } + filters := strings.Split(filterFlag, ",") + for _, filter := range filters { + filter = strings.TrimSpace(filter) + archs := append(make([]string, 0, len(devfile.Architectures)), devfile.Architectures...) + if len(archs) == 0 { + // Devfiles with no architectures are compatible with all architectures. + archs = append(archs, + string(apidevfile.AMD64), + string(apidevfile.ARM64), + string(apidevfile.PPC64LE), + string(apidevfile.S390X), + ) + } + containsArch := func(s string) bool { + for _, arch := range archs { + if strings.Contains(arch, s) { + return true + } + } + return false + } + if !strings.Contains(devfile.Name, filter) && !strings.Contains(devfile.Description, filter) && !containsArch(filter) { + continue devfileLoop } - return false - } - if !strings.Contains(devfile.Name, filterFlag) && !strings.Contains(devfile.Description, filterFlag) && !containsArch(filterFlag) { - continue } } diff --git a/tests/documentation/command-reference/doc_command_reference_init_test.go b/tests/documentation/command-reference/doc_command_reference_init_test.go index ea96a5a2f..d711b0318 100644 --- a/tests/documentation/command-reference/doc_command_reference_init_test.go +++ b/tests/documentation/command-reference/doc_command_reference_init_test.go @@ -32,6 +32,9 @@ var _ = Describe("doc command reference odo init", Label(helper.LabelNoCluster), It("Empty directory", func() { args := []string{"odo", "init"} out, err := helper.RunInteractive(args, []string{"ODO_LOG_LEVEL=0"}, func(ctx helper.InteractiveContext) { + helper.ExpectString(ctx, "Select architectures") + helper.SendLine(ctx, "") + helper.ExpectString(ctx, "Select language") helper.SendLine(ctx, "Java") diff --git a/tests/documentation/user-guides/doc_user_guides_quickstart_test.go b/tests/documentation/user-guides/doc_user_guides_quickstart_test.go index a1748ac7d..e7057c0b2 100644 --- a/tests/documentation/user-guides/doc_user_guides_quickstart_test.go +++ b/tests/documentation/user-guides/doc_user_guides_quickstart_test.go @@ -166,6 +166,9 @@ var _ = Describe("User guides: Quickstart test", func() { helper.ExpectString(ctx, "Is this correct?") helper.SendLine(ctx, "No") + helper.ExpectString(ctx, "Select architectures") + helper.SendLine(ctx, "") + helper.ExpectString(ctx, "Select language") helper.SendLine(ctx, ".") diff --git a/tests/e2escenarios/e2e_test.go b/tests/e2escenarios/e2e_test.go index e8559c498..8cac96faf 100644 --- a/tests/e2escenarios/e2e_test.go +++ b/tests/e2escenarios/e2e_test.go @@ -69,6 +69,9 @@ var _ = Describe("E2E Test", func() { command := []string{"odo", "init"} _, err := helper.RunInteractive(command, nil, func(ctx helper.InteractiveContext) { + helper.ExpectString(ctx, "Select architectures") + helper.SendLine(ctx, "") + helper.ExpectString(ctx, "Select language") helper.SendLine(ctx, "JavaScript") diff --git a/tests/helper/helper_documentation.go b/tests/helper/helper_documentation.go index a42716a8e..16776453d 100644 --- a/tests/helper/helper_documentation.go +++ b/tests/helper/helper_documentation.go @@ -35,6 +35,11 @@ func StripSpinner(docString string) (returnString string) { line := sc.Text() // trim any special character present in the line line = strings.TrimFunc(line, unicode.IsSpace) + + if len(line) == 0 { + continue + } + // This check is to avoid spinner statements in the cmd output // currently it does so for init and dev // e.g. " • Syncing file changes ..." @@ -92,6 +97,9 @@ func GetMDXContent(filePath string) (mdxContent string) { for fileScanner.Scan() { line := fileScanner.Text() line = strings.TrimFunc(line, unicode.IsSpace) + if len(line) == 0 { + continue + } mdxContent += line + "\n" } diff --git a/tests/integration/interactive_deploy_test.go b/tests/integration/interactive_deploy_test.go index f0fe30b7f..03f201573 100644 --- a/tests/integration/interactive_deploy_test.go +++ b/tests/integration/interactive_deploy_test.go @@ -159,7 +159,7 @@ var _ = Describe("odo deploy interactive command tests", func() { It("should not fail but fallback to the interactive mode", func() { _, err := helper.RunInteractive([]string{"odo", "deploy"}, nil, func(ctx helper.InteractiveContext) { helper.ExpectString(ctx, "Could not determine a Devfile based on the files in the current directory") - helper.ExpectString(ctx, "Select language") + helper.ExpectString(ctx, "Select architectures") ctx.StopCommand() }) Expect(err).Should(HaveOccurred()) diff --git a/tests/integration/interactive_dev_test.go b/tests/integration/interactive_dev_test.go index f2cf11016..34db1813e 100644 --- a/tests/integration/interactive_dev_test.go +++ b/tests/integration/interactive_dev_test.go @@ -165,6 +165,9 @@ var _ = Describe("odo dev interactive command tests", func() { output, _ := helper.RunInteractive([]string{"odo", "dev", "--random-ports"}, nil, func(ctx helper.InteractiveContext) { helper.ExpectString(ctx, "Could not determine a Devfile based on the files in the current directory") + helper.ExpectString(ctx, "Select architectures") + helper.SendLine(ctx, "") + helper.ExpectString(ctx, "Select language") helper.SendLine(ctx, "Python") diff --git a/tests/integration/interactive_init_test.go b/tests/integration/interactive_init_test.go index 1895db876..0a0b4ae3d 100644 --- a/tests/integration/interactive_init_test.go +++ b/tests/integration/interactive_init_test.go @@ -56,6 +56,9 @@ var _ = Describe("odo init interactive command tests", func() { helper.ExpectString(ctx, messages.InteractiveModeEnabled) }) + helper.ExpectString(ctx, "Select architectures") + helper.SendLine(ctx, "") + helper.ExpectString(ctx, "Select language") helper.SendLine(ctx, "Go") @@ -97,6 +100,9 @@ var _ = Describe("odo init interactive command tests", func() { command := []string{"odo", "init"} output, err := helper.RunInteractive(command, nil, func(ctx helper.InteractiveContext) { + helper.ExpectString(ctx, "Select architectures") + helper.SendLine(ctx, "") + helper.ExpectString(ctx, "Select language") helper.SendLine(ctx, "Javascript") @@ -148,6 +154,9 @@ var _ = Describe("odo init interactive command tests", func() { command := []string{"odo", "init"} _, err := helper.RunInteractive(command, nil, func(ctx helper.InteractiveContext) { + helper.ExpectString(ctx, "Select architectures") + helper.SendLine(ctx, "") + helper.ExpectString(ctx, "Select language") helper.SendLine(ctx, "Go") @@ -192,6 +201,9 @@ var _ = Describe("odo init interactive command tests", func() { output, err := helper.RunInteractive(command, nil, func(ctx helper.InteractiveContext) { + helper.ExpectString(ctx, "Select architectures") + helper.SendLine(ctx, "") + helper.ExpectString(ctx, "Select language") helper.SendLine(ctx, "Go") @@ -230,6 +242,9 @@ var _ = Describe("odo init interactive command tests", func() { helper.ExpectString(ctx, messages.InteractiveModeEnabled) }) + helper.ExpectString(ctx, "Select architectures") + helper.SendLine(ctx, "") + helper.ExpectString(ctx, "Select language") helper.SendLine(ctx, "Go") @@ -265,6 +280,9 @@ var _ = Describe("odo init interactive command tests", func() { output, err := helper.RunInteractive(command, nil, func(ctx helper.InteractiveContext) { + helper.ExpectString(ctx, "Select architectures") + helper.SendLine(ctx, "") + helper.ExpectString(ctx, "Select language") helper.SendLine(ctx, "Go") @@ -301,6 +319,9 @@ var _ = Describe("odo init interactive command tests", func() { helper.ExpectString(ctx, messages.InteractiveModeEnabled) }) + helper.ExpectString(ctx, "Select architectures") + helper.SendLine(ctx, "") + helper.ExpectString(ctx, "Select language") helper.SendLine(ctx, "java") @@ -395,6 +416,9 @@ var _ = Describe("odo init interactive command tests", func() { welcomingMsgs := strings.Split(odolog.Stitle(messages.InitializingNewComponent, messages.NoSourceCodeDetected, "odo version: "+version.VERSION), "\n") output, err := testRunner(language, welcomingMsgs, func(ctx helper.InteractiveContext) { + helper.ExpectString(ctx, "Select architectures") + helper.SendLine(ctx, "") + helper.ExpectString(ctx, "Select language") helper.SendLine(ctx, language) @@ -547,6 +571,9 @@ var _ = Describe("odo init interactive command tests", func() { output, err := helper.RunInteractive([]string{"odo", "init"}, nil, func(ctx helper.InteractiveContext) { + helper.ExpectString(ctx, "Select architectures") + helper.SendLine(ctx, "") + helper.ExpectString(ctx, "Select language") helper.SendLine(ctx, ".NET") @@ -643,7 +670,7 @@ var _ = Describe("odo init interactive command tests", func() { _, err := helper.RunInteractive([]string{"odo", "init"}, nil, func(ctx helper.InteractiveContext) { helper.ExpectString(ctx, "Could not determine a Devfile based on the files in the current directory") - helper.ExpectString(ctx, "Select language") + helper.ExpectString(ctx, "Select architectures") ctx.StopCommand() }) Expect(err).Should(HaveOccurred()) @@ -689,6 +716,9 @@ spec: } output, err := helper.RunInteractive([]string{"odo", "init"}, nil, func(ctx helper.InteractiveContext) { + helper.ExpectString(ctx, "Select architectures") + helper.SendLine(ctx, "") + helper.ExpectString(ctx, "Select language") helper.SendLine(ctx, "Java")