Go: Bump github.com/devfile/alizer from 1.0.1 to 1.2.1 (#7126)

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Armel Soro <asoro@redhat.com>
This commit is contained in:
dependabot[bot]
2023-10-23 18:09:52 +02:00
committed by GitHub
parent 4d6a785cff
commit d77ca3e95a
47 changed files with 1513 additions and 619 deletions

View File

@@ -53,7 +53,7 @@ func TestOdoAlizer(t *testing.T) {
path := "/"
alizerClient.EXPECT().DetectFramework(gomock.Any(), path).
Return(alizer.DetectedFramework{
Type: model.DevFileType{
Type: model.DevfileType{
Name: "framework-name",
},
DefaultVersion: "1.1.1",
@@ -94,7 +94,7 @@ func TestOdoAlizer(t *testing.T) {
path := "/"
alizerClient.EXPECT().DetectFramework(gomock.Any(), path).
Return(alizer.DetectedFramework{
Type: model.DevFileType{
Type: model.DevfileType{
Name: "framework-name",
},
DefaultVersion: "1.1.1",

2
go.mod
View File

@@ -8,7 +8,7 @@ require (
github.com/AlecAivazis/survey/v2 v2.3.5
github.com/Xuanwo/go-locale v1.1.0
github.com/blang/semver v3.5.1+incompatible
github.com/devfile/alizer v1.0.1
github.com/devfile/alizer v1.2.1
github.com/devfile/api/v2 v2.2.1-alpha.0.20230413012049-a6c32fca0dbd
github.com/devfile/library/v2 v2.2.1-0.20230524160049-04a8b3fc66c0
github.com/devfile/registry-support/index/generator v0.0.0-20230322155332-33914affc83b

4
go.sum generated
View File

@@ -362,8 +362,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE=
github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
github.com/devfile/alizer v1.0.1 h1:VcqX8kVGvCbRTzt66S2F132q83e9vbkV2U6ShmUl3Ew=
github.com/devfile/alizer v1.0.1/go.mod h1:s08rBJWzwGFAAhdrJP0LduJDfSESfaVn/3dkUlK7Kmc=
github.com/devfile/alizer v1.2.1 h1:0CrHYGjT0xTw5CYoK/U1pYGmcpKxw9YUUVaqJWF3pZM=
github.com/devfile/alizer v1.2.1/go.mod h1:kRCsbXuzCwXIbnSR4xpVZDr8Pl8j01rl+gPfkukbf/c=
github.com/devfile/api/v2 v2.0.0-20211021164004-dabee4e633ed/go.mod h1:d99eTN6QxgzihOOFyOZA+VpUyD4Q1pYRYHZ/ci9J96Q=
github.com/devfile/api/v2 v2.0.0-20220117162434-6e6e6a8bc14c/go.mod h1:d99eTN6QxgzihOOFyOZA+VpUyD4Q1pYRYHZ/ci9J96Q=
github.com/devfile/api/v2 v2.2.0/go.mod h1:dN7xFrOVG+iPqn4UKGibXLd5oVsdE8XyK9OEb5JL3aI=

View File

@@ -30,13 +30,13 @@ 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) (DetectedFramework, error) {
types := []model.DevFileType{}
types := []model.DevfileType{}
components, err := o.registryClient.ListDevfileStacks(ctx, "", "", "", false, false)
if err != nil {
return DetectedFramework{}, err
}
for _, component := range components.Items {
types = append(types, model.DevFileType{
types = append(types, model.DevfileType{
Name: component.Name,
Language: component.Language,
ProjectType: component.ProjectType,
@@ -144,7 +144,7 @@ func (o *Alizer) DetectPorts(path string) ([]int, error) {
return components[0].Ports, nil
}
func NewDetectionResult(typ model.DevFileType, registry api.Registry, appPorts []int, devfileVersion, name string) *api.DetectionResult {
func NewDetectionResult(typ model.DevfileType, registry api.Registry, appPorts []int, devfileVersion, name string) *api.DetectionResult {
return &api.DetectionResult{
Devfile: typ.Name,
DevfileRegistry: registry.Name,

View File

@@ -4,11 +4,12 @@ import (
"context"
"github.com/devfile/alizer/pkg/apis/model"
"github.com/redhat-developer/odo/pkg/api"
)
type DetectedFramework struct {
Type model.DevFileType
Type model.DevfileType
DefaultVersion string
Registry api.Registry
Architectures []string

View File

@@ -95,7 +95,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(alizer.DetectedFramework{
Type: model.DevFileType{
Type: model.DevfileType{
Name: "a-devfile-name",
},
DefaultVersion: "1.0.0",
@@ -124,7 +124,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(alizer.DetectedFramework{
Type: model.DevFileType{
Type: model.DevfileType{
Name: "a-devfile-name",
},
DefaultVersion: "1.0.0",
@@ -178,7 +178,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(alizer.DetectedFramework{
Type: model.DevFileType{
Type: model.DevfileType{
Name: "a-devfile-name",
},
DefaultVersion: "1.0.0",

View File

@@ -3,6 +3,7 @@ package alizer
import (
"context"
"errors"
"fmt"
"github.com/redhat-developer/odo/pkg/alizer"
"github.com/redhat-developer/odo/pkg/api"
@@ -56,7 +57,8 @@ func (o *AlizerOptions) RunForJsonOutput(ctx context.Context) (out interface{},
workingDir := odocontext.GetWorkingDirectory(ctx)
detected, err := o.clientset.AlizerClient.DetectFramework(ctx, workingDir)
if err != nil {
return nil, err
//revive:disable:error-strings This is a top-level error message displayed as is to the end user
return nil, fmt.Errorf("No valid devfile found for project in %s: %w", workingDir, err)
}
appPorts, err := o.clientset.AlizerClient.DetectPorts(workingDir)
if err != nil {

View File

@@ -604,19 +604,23 @@ var _ = Describe("odo init interactive command tests", func() {
lines, err := helper.ExtractLines(output)
Expect(err).To(BeNil())
Expect(len(lines)).To(BeNumerically(">", 2))
Expect(lines[len(lines)-1]).To(Equal("Your new component 'my-dotnet-app' is ready in the current directory"))
Expect(len(lines)).To(BeNumerically(">", 2), output)
Expect(lines[len(lines)-1]).To(Equal("Your new component 'my-dotnet-app' is ready in the current directory"), output)
componentNameQuestionIdx, ok := helper.FindFirstElementIndexMatchingRegExp(lines, ".*Enter component name:.*")
Expect(ok).To(BeTrue())
Expect(ok).To(BeTrue(),
fmt.Sprintf("'Enter component name:' not found in output below:\n===OUTPUT===\n%s============\n", output))
starterProjectDownloadActionIdx, found := helper.FindFirstElementIndexMatchingRegExp(lines,
".*Downloading starter project \"([^\\s]+)\" \\[.*")
Expect(found).To(BeTrue())
Expect(starterProjectDownloadActionIdx).To(SatisfyAll(
Expect(found).To(BeTrue(),
fmt.Sprintf("'Downloading starter project \"([^\\s]+)\"' not found in output below:\n===OUTPUT===\n%s============\n", output))
Expect(starterProjectDownloadActionIdx).To(
SatisfyAll(
Not(BeZero()),
// #5495: component name question should be displayed before starter project is actually downloaded
BeNumerically(">", componentNameQuestionIdx),
), "Action 'Downloading starter project' should have been displayed after the last interactive question ('Enter component name')")
BeNumerically(">", componentNameQuestionIdx)),
fmt.Sprintf("Action 'Downloading starter project' should have been displayed after the last interactive question ('Enter component name').\n===OUTPUT===\n%s============\n",
output))
Expect(helper.ListFilesInDir(commonVar.Context)).To(ContainElements("devfile.yaml"))
})

View File

@@ -0,0 +1,52 @@
//
// Copyright 2023 Red Hat, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package enricher
import (
"context"
"github.com/devfile/alizer/pkg/apis/model"
)
type DockerEnricher struct{}
type DockerFrameworkDetector interface {
DoPortsDetection(component *model.Component, ctx *context.Context)
}
func (d DockerEnricher) GetSupportedLanguages() []string {
return []string{"dockerfile"}
}
func (d DockerEnricher) DoEnrichLanguage(language *model.Language, _ *[]string) {
// The Dockerfile language does not contain frameworks
return
}
func (d DockerEnricher) DoEnrichComponent(component *model.Component, _ model.DetectionSettings, _ *context.Context) {
projectName := GetDefaultProjectName(component.Path)
component.Name = projectName
var ports []int
ports = GetPortsFromDockerFile(component.Path)
if len(ports) > 0 {
component.Ports = ports
}
return
}
func (d DockerEnricher) IsConfigValidForComponentDetection(language string, config string) bool {
return IsConfigurationValidForLanguage(language, config)
}

View File

@@ -19,18 +19,14 @@ package enricher
import (
"context"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"github.com/devfile/alizer/pkg/apis/model"
"github.com/devfile/alizer/pkg/utils"
"github.com/devfile/alizer/pkg/utils/langfiles"
"github.com/moby/buildkit/frontend/dockerfile/parser"
"gopkg.in/yaml.v3"
)
@@ -93,6 +89,7 @@ func getEnrichers() []Enricher {
&DotNetEnricher{},
&GoEnricher{},
&PHPEnricher{},
&DockerEnricher{},
}
}
@@ -123,7 +120,7 @@ func GetDefaultProjectName(path string) string {
// GetPortsFromDockerFile returns a slice of port numbers from Dockerfiles in the given directory.
func GetPortsFromDockerFile(root string) []int {
locations := getLocations(root)
locations := utils.GetLocations(root)
for _, location := range locations {
filePath := filepath.Join(root, location)
cleanFilePath := filepath.Clean(filePath)
@@ -135,57 +132,12 @@ func GetPortsFromDockerFile(root string) []int {
}
return nil
}()
return getPortsFromReader(file)
return utils.ReadPortsFromDockerfile(file)
}
}
return []int{}
}
func getLocations(root string) []string {
locations := []string{"Dockerfile", "Containerfile"}
dirItems, err := ioutil.ReadDir(root)
if err != nil {
return locations
}
for _, item := range dirItems {
if strings.HasPrefix(item.Name(), ".") {
continue
}
tmpPath := fmt.Sprintf("%s%s", root, item.Name())
fileInfo, err := os.Stat(tmpPath)
if err != nil {
continue
}
if fileInfo.IsDir() {
locations = append(locations, fmt.Sprintf("%s/%s", item.Name(), "Dockerfile"))
locations = append(locations, fmt.Sprintf("%s/%s", item.Name(), "Containerfile"))
}
}
return locations
}
// getPortsFromReader returns a slice of port numbers.
func getPortsFromReader(file io.Reader) []int {
var ports []int
res, err := parser.Parse(file)
if err != nil {
return ports
}
for _, child := range res.AST.Children {
// check for the potential port number in a Dockerfile/Containerfile
if strings.ToLower(child.Value) == "expose" {
for n := child.Next; n != nil; n = n.Next {
if port, err := strconv.Atoi(n.Value); err == nil {
ports = append(ports, port)
}
}
}
}
return ports
}
// GetPortsFromDockerComposeFile returns a slice of port numbers from a compose file.
func GetPortsFromDockerComposeFile(componentPath string, settings model.DetectionSettings) []int {
var ports []int

View File

@@ -15,7 +15,7 @@ import (
"context"
"encoding/xml"
"fmt"
"io/ioutil"
"io"
"os"
"path/filepath"
"strings"
@@ -31,6 +31,11 @@ func (d DotNetDetector) GetSupportedFrameworks() []string {
return []string{""}
}
func (d DotNetDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
// not implemented yet
return []model.ApplicationFileInfo{}
}
// DoFrameworkDetection uses configFilePath to check for the name of the framework
func (d DotNetDetector) DoFrameworkDetection(language *model.Language, configFilePath string) {
framework := getFrameworks(configFilePath)
@@ -52,6 +57,7 @@ func (d DotNetDetector) DoFrameworkDetection(language *model.Language, configFil
}
func (d DotNetDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
// not implemented yet
}
func getFrameworks(configFilePath string) string {
@@ -60,7 +66,7 @@ func getFrameworks(configFilePath string) string {
if err != nil {
return ""
}
byteValue, _ := ioutil.ReadAll(xmlFile)
byteValue, _ := io.ReadAll(xmlFile)
var proj schema.DotNetProject
err = xml.Unmarshal(byteValue, &proj)

View File

@@ -26,6 +26,17 @@ func (b BeegoDetector) GetSupportedFrameworks() []string {
return []string{"Beego"}
}
func (b BeegoDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
return []model.ApplicationFileInfo{
{
Context: ctx,
Root: componentPath,
Dir: "conf",
File: "app.conf",
},
}
}
// DoFrameworkDetection uses a tag to check for the framework name
func (b BeegoDetector) DoFrameworkDetection(language *model.Language, goMod *modfile.File) {
if hasFramework(goMod.Require, "github.com/beego/beego") {
@@ -40,15 +51,16 @@ type ApplicationPropertiesFile struct {
// DoPortsDetection searches for the port in conf/app.conf
func (b BeegoDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
bytes, err := utils.ReadAnyApplicationFile(component.Path, []model.ApplicationFileInfo{
{
Dir: "conf",
File: "app.conf",
},
}, ctx)
fileContents, err := utils.GetApplicationFileContents(b.GetApplicationFileInfos(component.Path, ctx))
if err != nil {
return
}
for _, fileContent := range fileContents {
re := regexp.MustCompile(`httpport\s*=\s*(\d+)`)
component.Ports = utils.FindAllPortsSubmatch(re, string(bytes), 1)
ports := utils.FindAllPortsSubmatch(re, fileContent, 1)
if len(ports) > 0 {
component.Ports = ports
return
}
}
}

View File

@@ -26,6 +26,14 @@ func (e EchoDetector) GetSupportedFrameworks() []string {
return []string{"Echo"}
}
func (e EchoDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
files, err := utils.GetCachedFilePathsFromRoot(componentPath, ctx)
if err != nil {
return []model.ApplicationFileInfo{}
}
return utils.GenerateApplicationFileFromFilters(files, componentPath, ".go", ctx)
}
// DoFrameworkDetection uses a tag to check for the framework name
func (e EchoDetector) DoFrameworkDetection(language *model.Language, goMod *modfile.File) {
if hasFramework(goMod.Require, "github.com/labstack/echo") {
@@ -34,11 +42,10 @@ func (e EchoDetector) DoFrameworkDetection(language *model.Language, goMod *modf
}
func (e EchoDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
files, err := utils.GetCachedFilePathsFromRoot(component.Path, ctx)
fileContents, err := utils.GetApplicationFileContents(e.GetApplicationFileInfos(component.Path, ctx))
if err != nil {
return
}
matchRegexRules := model.PortMatchRules{
MatchIndexRegexes: []model.PortMatchRule{
{
@@ -58,8 +65,11 @@ func (e EchoDetector) DoPortsDetection(component *model.Component, ctx *context.
},
}
ports := GetPortFromFilesGo(matchRegexRules, files)
for _, fileContent := range fileContents {
ports := GetPortFromFileGo(matchRegexRules, fileContent)
if len(ports) > 0 {
component.Ports = ports
return
}
}
}

View File

@@ -26,6 +26,14 @@ func (f FastHttpDetector) GetSupportedFrameworks() []string {
return []string{"FastHttp"}
}
func (f FastHttpDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
files, err := utils.GetCachedFilePathsFromRoot(componentPath, ctx)
if err != nil {
return []model.ApplicationFileInfo{}
}
return utils.GenerateApplicationFileFromFilters(files, componentPath, ".go", ctx)
}
// DoFrameworkDetection uses a tag to check for the framework name
func (f FastHttpDetector) DoFrameworkDetection(language *model.Language, goMod *modfile.File) {
if hasFramework(goMod.Require, "github.com/valyala/fasthttp") {
@@ -34,7 +42,7 @@ func (f FastHttpDetector) DoFrameworkDetection(language *model.Language, goMod *
}
func (f FastHttpDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
files, err := utils.GetCachedFilePathsFromRoot(component.Path, ctx)
fileContents, err := utils.GetApplicationFileContents(f.GetApplicationFileInfos(component.Path, ctx))
if err != nil {
return
}
@@ -47,8 +55,11 @@ func (f FastHttpDetector) DoPortsDetection(component *model.Component, ctx *cont
},
},
}
ports := GetPortFromFilesGo(matchRegexRules, files)
for _, fileContent := range fileContents {
ports := GetPortFromFileGo(matchRegexRules, fileContent)
if len(ports) > 0 {
component.Ports = ports
return
}
}
}

View File

@@ -26,6 +26,14 @@ func (g GinDetector) GetSupportedFrameworks() []string {
return []string{"Gin"}
}
func (g GinDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
files, err := utils.GetCachedFilePathsFromRoot(componentPath, ctx)
if err != nil {
return []model.ApplicationFileInfo{}
}
return utils.GenerateApplicationFileFromFilters(files, componentPath, ".go", ctx)
}
// DoFrameworkDetection uses a tag to check for the framework name
func (g GinDetector) DoFrameworkDetection(language *model.Language, goMod *modfile.File) {
if hasFramework(goMod.Require, "github.com/gin-gonic/gin") {
@@ -34,7 +42,7 @@ func (g GinDetector) DoFrameworkDetection(language *model.Language, goMod *modfi
}
func (g GinDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
files, err := utils.GetCachedFilePathsFromRoot(component.Path, ctx)
fileContents, err := utils.GetApplicationFileContents(g.GetApplicationFileInfos(component.Path, ctx))
if err != nil {
return
}
@@ -48,8 +56,11 @@ func (g GinDetector) DoPortsDetection(component *model.Component, ctx *context.C
},
}
ports := GetPortFromFilesGo(matchRegexRules, files)
for _, fileContent := range fileContents {
ports := GetPortFromFileGo(matchRegexRules, fileContent)
if len(ports) > 0 {
component.Ports = ports
return
}
}
}

View File

@@ -13,8 +13,6 @@ package enricher
import (
"context"
"os"
"path/filepath"
"regexp"
"strings"
@@ -38,7 +36,11 @@ func DoGoPortsDetection(component *model.Component, ctx *context.Context) {
if err != nil {
return
}
appFileInfos := utils.GenerateApplicationFileFromFilters(files, component.Path, ".go", ctx)
fileContents, err := utils.GetApplicationFileContents(appFileInfos)
if err != nil {
return
}
matchRegexRules := model.PortMatchRules{
MatchIndexRegexes: []model.PortMatchRule{
{
@@ -58,9 +60,12 @@ func DoGoPortsDetection(component *model.Component, ctx *context.Context) {
},
}
ports := GetPortFromFilesGo(matchRegexRules, files)
for _, fileContent := range fileContents {
ports := GetPortFromFileGo(matchRegexRules, fileContent)
if len(ports) > 0 {
component.Ports = ports
return
}
}
}
@@ -135,20 +140,3 @@ func GetPortWithMatchIndexesGo(content string, matchIndexes []int, toBeReplaced
return -1
}
// GetPortFromFilesGo loops through a list of paths and tries to find a port matching the
// given set PortMatchRules
func GetPortFromFilesGo(matchRegexRules model.PortMatchRules, files []string) []int {
for _, file := range files {
cleanFile := filepath.Clean(file)
bytes, err := os.ReadFile(cleanFile)
if err != nil {
continue
}
ports := GetPortFromFileGo(matchRegexRules, string(bytes))
if len(ports) > 0 {
return ports
}
}
return []int{}
}

View File

@@ -26,6 +26,14 @@ func (g GoFiberDetector) GetSupportedFrameworks() []string {
return []string{"GoFiber"}
}
func (g GoFiberDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
files, err := utils.GetCachedFilePathsFromRoot(componentPath, ctx)
if err != nil {
return []model.ApplicationFileInfo{}
}
return utils.GenerateApplicationFileFromFilters(files, componentPath, ".go", ctx)
}
// DoFrameworkDetection uses a tag to check for the framework name
func (g GoFiberDetector) DoFrameworkDetection(language *model.Language, goMod *modfile.File) {
if hasFramework(goMod.Require, "github.com/gofiber/fiber") {
@@ -34,7 +42,7 @@ func (g GoFiberDetector) DoFrameworkDetection(language *model.Language, goMod *m
}
func (g GoFiberDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
files, err := utils.GetCachedFilePathsFromRoot(component.Path, ctx)
fileContents, err := utils.GetApplicationFileContents(g.GetApplicationFileInfos(component.Path, ctx))
if err != nil {
return
}
@@ -47,8 +55,12 @@ func (g GoFiberDetector) DoPortsDetection(component *model.Component, ctx *conte
},
},
}
ports := GetPortFromFilesGo(matchRegexRules, files)
for _, fileContent := range fileContents {
ports := GetPortFromFileGo(matchRegexRules, fileContent)
if len(ports) > 0 {
component.Ports = ports
return
}
}
}

View File

@@ -26,6 +26,14 @@ func (m MuxDetector) GetSupportedFrameworks() []string {
return []string{"Mux"}
}
func (m MuxDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
files, err := utils.GetCachedFilePathsFromRoot(componentPath, ctx)
if err != nil {
return []model.ApplicationFileInfo{}
}
return utils.GenerateApplicationFileFromFilters(files, componentPath, ".go", ctx)
}
// DoFrameworkDetection uses a tag to check for the framework name
func (m MuxDetector) DoFrameworkDetection(language *model.Language, goMod *modfile.File) {
if hasFramework(goMod.Require, "github.com/gorilla/mux") {
@@ -34,7 +42,7 @@ func (m MuxDetector) DoFrameworkDetection(language *model.Language, goMod *modfi
}
func (m MuxDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
files, err := utils.GetCachedFilePathsFromRoot(component.Path, ctx)
fileContents, err := utils.GetApplicationFileContents(m.GetApplicationFileInfos(component.Path, ctx))
if err != nil {
return
}
@@ -54,8 +62,11 @@ func (m MuxDetector) DoPortsDetection(component *model.Component, ctx *context.C
},
}
ports := GetPortFromFilesGo(matchRegexRules, files)
for _, fileContent := range fileContents {
ports := GetPortFromFileGo(matchRegexRules, fileContent)
if len(ports) > 0 {
component.Ports = ports
return
}
}
}

View File

@@ -15,6 +15,7 @@ import (
"regexp"
"strings"
"github.com/devfile/alizer/pkg/schema"
"github.com/devfile/alizer/pkg/utils"
)
@@ -36,13 +37,8 @@ func hasFramework(configFile, groupId, artifactId string) (bool, error) {
// GetPortsForJBossFrameworks tries to detect any port information inside javaOpts of configuration
// of a given profiles plugin
func GetPortsForJBossFrameworks(pomFilePath, pluginArtifactId, pluginGroupId string) string {
func GetPortsForJBossFrameworks(pom schema.Pom, pluginArtifactId string, pluginGroupId string) string {
portPlaceholder := ""
pom, err := utils.GetPomFileContent(pomFilePath)
if err != nil {
return portPlaceholder
}
re := regexp.MustCompile(`jboss.https?.port=\d*`)
// Check for port configuration inside profiles
for _, profile := range pom.Profiles.Profile {

View File

@@ -13,8 +13,10 @@ package enricher
import (
"context"
"encoding/xml"
"github.com/devfile/alizer/pkg/apis/model"
"github.com/devfile/alizer/pkg/schema"
"github.com/devfile/alizer/pkg/utils"
)
@@ -24,6 +26,17 @@ func (o JBossEAPDetector) GetSupportedFrameworks() []string {
return []string{"JBoss EAP"}
}
func (o JBossEAPDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
return []model.ApplicationFileInfo{
{
Context: ctx,
Root: componentPath,
Dir: "",
File: "pom.xml",
},
}
}
// DoFrameworkDetection uses the groupId and artifactId to check for the framework name
func (o JBossEAPDetector) DoFrameworkDetection(language *model.Language, config string) {
if hasFwk, _ := hasFramework(config, "org.jboss.eap.plugins", "eap-maven-plugin"); hasFwk {
@@ -34,14 +47,26 @@ func (o JBossEAPDetector) DoFrameworkDetection(language *model.Language, config
func (o JBossEAPDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
ports := []int{}
// Fetch the content of xml for this component
paths, err := utils.GetCachedFilePathsFromRoot(component.Path, ctx)
if err != nil {
appFileInfos := o.GetApplicationFileInfos(component.Path, ctx)
if len(appFileInfos) == 0 {
return
}
pomXML := utils.GetFile(&paths, "pom.xml")
portPlaceholder := GetPortsForJBossFrameworks(pomXML, "eap-maven-plugin", "org.jboss.eap.plugins")
for _, appFileInfo := range appFileInfos {
fileBytes, err := utils.GetApplicationFileBytes(appFileInfo)
if err != nil {
continue
}
var pom schema.Pom
err = xml.Unmarshal(fileBytes, &pom)
if err != nil {
continue
}
portPlaceholder := GetPortsForJBossFrameworks(pom, "eap-maven-plugin", "org.jboss.eap.plugins")
if portPlaceholder == "" {
return
continue
}
if port, err := utils.GetValidPort(portPlaceholder); err == nil {
@@ -53,3 +78,4 @@ func (o JBossEAPDetector) DoPortsDetection(component *model.Component, ctx *cont
return
}
}
}

View File

@@ -22,22 +22,27 @@ import (
type MicronautDetector struct{}
type MicronautApplicationProps struct {
Micronaut struct {
Server struct {
Port int `yaml:"port,omitempty"`
SSL struct {
Enabled bool `yaml:"enabled,omitempty"`
Port int `yaml:"port,omitempty"`
} `yaml:"ssl,omitempty"`
} `yaml:"server,omitempty"`
} `yaml:"micronaut,omitempty"`
}
func (m MicronautDetector) GetSupportedFrameworks() []string {
return []string{"Micronaut"}
}
func (m MicronautDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
return []model.ApplicationFileInfo{
{
Context: ctx,
Root: componentPath,
Dir: "src/main/resources",
File: "application.yml",
},
{
Context: ctx,
Root: componentPath,
Dir: "src/main/resources",
File: "application.yaml",
},
}
}
// DoFrameworkDetection uses the groupId to check for the framework name
func (m MicronautDetector) DoFrameworkDetection(language *model.Language, config string) {
if hasFwk, _ := hasFramework(config, "io.micronaut", ""); hasFwk {
@@ -54,28 +59,36 @@ func (m MicronautDetector) DoPortsDetection(component *model.Component, ctx *con
return
}
bytes, err := utils.ReadAnyApplicationFile(component.Path, []model.ApplicationFileInfo{
{
Dir: "src/main/resources",
File: "application.yml",
},
{
Dir: "src/main/resources",
File: "application.yaml",
},
}, ctx)
if err != nil {
return
}
ports = getMicronautPortsFromBytes(bytes)
// check if port is set on dockerfile as env var
ports = getMicronautPortsFromEnvDockerfile(component.Path)
if len(ports) > 0 {
component.Ports = ports
return
}
// check source code
appFileInfos := m.GetApplicationFileInfos(component.Path, ctx)
if len(appFileInfos) == 0 {
return
}
for _, appFileInfo := range appFileInfos {
fileBytes, err := utils.GetApplicationFileBytes(appFileInfo)
if err != nil {
continue
}
ports = getMicronautPortsFromBytes(fileBytes)
if len(ports) > 0 {
component.Ports = ports
return
}
}
}
func getMicronautPortsFromBytes(bytes []byte) []int {
var ports []int
var data MicronautApplicationProps
var data model.MicronautApplicationProps
err := yaml.Unmarshal(bytes, &data)
if err != nil {
return []int{}
@@ -97,3 +110,24 @@ func getMicronautPortsFromEnvs() []int {
}
return utils.GetValidPortsFromEnvs(envs)
}
func getMicronautPortsFromEnvDockerfile(path string) []int {
envVars, err := utils.GetEnvVarsFromDockerFile(path)
if err != nil {
return nil
}
sslEnabled := ""
envs := []string{"MICRONAUT_SERVER_PORT"}
for _, envVar := range envVars {
if envVar.Name == "MICRONAUT_SERVER_SSL_ENABLED" {
sslEnabled = envVar.Value
break
}
}
if sslEnabled == "true" {
envs = append(envs, "MICRONAUT_SERVER_SSL_PORT")
}
return utils.GetValidPortsFromEnvDockerfile(envs, envVars)
}

View File

@@ -21,17 +21,27 @@ import (
type OpenLibertyDetector struct{}
type ServerXml struct {
HttpEndpoint struct {
HttpPort string `xml:"httpPort,attr"`
HttpsPort string `xml:"httpsPort,attr"`
} `xml:"httpEndpoint"`
}
func (o OpenLibertyDetector) GetSupportedFrameworks() []string {
return []string{"OpenLiberty"}
}
func (o OpenLibertyDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
return []model.ApplicationFileInfo{
{
Context: ctx,
Root: componentPath,
Dir: "",
File: "server.xml",
},
{
Context: ctx,
Root: componentPath,
Dir: "src/main/liberty/config",
File: "server.xml",
},
}
}
// DoFrameworkDetection uses the groupId to check for the framework name
func (o OpenLibertyDetector) DoFrameworkDetection(language *model.Language, config string) {
if hasFwk, _ := hasFramework(config, "io.openliberty", ""); hasFwk {
@@ -41,26 +51,26 @@ func (o OpenLibertyDetector) DoFrameworkDetection(language *model.Language, conf
// DoPortsDetection searches for the port in src/main/liberty/config/server.xml and /server.xml
func (o OpenLibertyDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
bytes, err := utils.ReadAnyApplicationFile(component.Path, []model.ApplicationFileInfo{
{
Dir: "",
File: "server.xml",
},
{
Dir: "src/main/liberty/config",
File: "server.xml",
},
}, ctx)
if err != nil {
appFileInfos := o.GetApplicationFileInfos(component.Path, ctx)
if len(appFileInfos) == 0 {
return
}
var data ServerXml
err = xml.Unmarshal(bytes, &data)
for _, appFileInfo := range appFileInfos {
fileBytes, err := utils.GetApplicationFileBytes(appFileInfo)
if err != nil {
return
continue
}
var data model.OpenLibertyServerXml
err = xml.Unmarshal(fileBytes, &data)
if err != nil {
continue
}
ports := utils.GetValidPorts([]string{data.HttpEndpoint.HttpPort, data.HttpEndpoint.HttpsPort})
if len(ports) > 0 {
component.Ports = ports
return
}
}
}

View File

@@ -14,7 +14,6 @@ package enricher
import (
"context"
"errors"
"io/ioutil"
"os"
"path/filepath"
@@ -25,24 +24,33 @@ import (
type QuarkusDetector struct{}
type QuarkusApplicationYaml struct {
Quarkus QuarkusHttp `yaml:"quarkus,omitempty"`
}
type QuarkusHttp struct {
Http QuarkusHttpPort `yaml:"http,omitempty"`
}
type QuarkusHttpPort struct {
Port int `yaml:"port,omitempty"`
InsecureRequests string `yaml:"insecure-requests,omitempty"`
SSLPort int `yaml:"ssl-port,omitempty"`
}
func (q QuarkusDetector) GetSupportedFrameworks() []string {
return []string{"Quarkus"}
}
func (q QuarkusDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
return []model.ApplicationFileInfo{
{
Context: ctx,
Root: componentPath,
Dir: "src/main/resources",
File: "application.properties",
},
{
Context: ctx,
Root: componentPath,
Dir: "src/main/resources",
File: "application.yml",
},
{
Context: ctx,
Root: componentPath,
Dir: "src/main/resources",
File: "application.yaml",
},
}
}
// DoFrameworkDetection uses the groupId to check for the framework name
func (q QuarkusDetector) DoFrameworkDetection(language *model.Language, config string) {
if hasFwk, _ := hasFramework(config, "io.quarkus", ""); hasFwk {
@@ -59,6 +67,14 @@ func (q QuarkusDetector) DoPortsDetection(component *model.Component, ctx *conte
component.Ports = ports
return
}
// check if port is set on env var of a dockerfile
ports = getQuarkusPortsFromEnvDockerfile(component.Path)
if len(ports) > 0 {
component.Ports = ports
return
}
// check if port is set on .env file
insecureRequestEnabled := utils.GetStringValueFromEnvFile(component.Path, `QUARKUS_HTTP_INSECURE_REQUESTS=(\w*)`)
regexes := []string{`QUARKUS_HTTP_SSL_PORT=(\d*)`}
@@ -71,20 +87,13 @@ func (q QuarkusDetector) DoPortsDetection(component *model.Component, ctx *conte
return
}
applicationFile := utils.GetAnyApplicationFilePath(component.Path, []model.ApplicationFileInfo{
{
Dir: "src/main/resources",
File: "application.properties",
},
{
Dir: "src/main/resources",
File: "application.yml",
},
{
Dir: "src/main/resources",
File: "application.yaml",
},
}, ctx)
appFileInfos := q.GetApplicationFileInfos(component.Path, ctx)
if len(appFileInfos) == 0 {
return
}
// case: no port found as env var. Look into source code.
applicationFile := utils.GetAnyApplicationFilePath(component.Path, appFileInfos, ctx)
if applicationFile == "" {
return
}
@@ -110,6 +119,27 @@ func getQuarkusPortsFromEnvs() []int {
return utils.GetValidPortsFromEnvs(envs)
}
func getQuarkusPortsFromEnvDockerfile(path string) []int {
envVars, err := utils.GetEnvVarsFromDockerFile(path)
if err != nil {
return nil
}
insecureRequestEnabled := ""
envs := []string{"QUARKUS_HTTP_SSL_PORT"}
for _, envVar := range envVars {
if envVar.Name == "QUARKUS_HTTP_INSECURE_REQUESTS" {
insecureRequestEnabled = envVar.Value
break
}
}
if insecureRequestEnabled == "true" {
envs = append(envs, "QUARKUS_HTTP_PORT")
}
return utils.GetValidPortsFromEnvDockerfile(envs, envVars)
}
func getServerPortsFromQuarkusPropertiesFile(file string) ([]int, error) {
var ports []int
props, err := utils.ConvertPropertiesFileAsPathToMap(file)
@@ -135,11 +165,11 @@ func getServerPortsFromQuarkusPropertiesFile(file string) ([]int, error) {
}
func getServerPortsFromQuarkusApplicationYamlFile(file string) ([]int, error) {
yamlFile, err := ioutil.ReadFile(file)
yamlFile, err := os.ReadFile(filepath.Clean(file))
if err != nil {
return []int{}, err
}
var data QuarkusApplicationYaml
var data model.QuarkusApplicationYaml
err = yaml.Unmarshal(yamlFile, &data)
if err != nil {
return []int{}, err

View File

@@ -14,7 +14,7 @@ package enricher
import (
"context"
"errors"
"io/ioutil"
"os"
"path/filepath"
"github.com/devfile/alizer/pkg/apis/model"
@@ -24,19 +24,33 @@ import (
type SpringDetector struct{}
type ApplicationProsServer struct {
Server struct {
Port int `yaml:"port,omitempty"`
Http struct {
Port int `yaml:"port,omitempty"`
} `yaml:"http,omitempty"`
} `yaml:"server,omitempty"`
}
func (s SpringDetector) GetSupportedFrameworks() []string {
return []string{"Spring", "Spring Boot"}
}
func (s SpringDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
return []model.ApplicationFileInfo{
{
Context: ctx,
Root: componentPath,
Dir: "src/main/resources",
File: "application.properties",
},
{
Context: ctx,
Root: componentPath,
Dir: "src/main/resources",
File: "application.yml",
},
{
Context: ctx,
Root: componentPath,
Dir: "src/main/resources",
File: "application.yaml",
},
}
}
// DoFrameworkDetection uses the groupId to check for the framework name
func (s SpringDetector) DoFrameworkDetection(language *model.Language, config string) {
if hasFwk, _ := hasFramework(config, "org.springframework", ""); hasFwk {
@@ -47,30 +61,31 @@ func (s SpringDetector) DoFrameworkDetection(language *model.Language, config st
// DoPortsDetection searches for ports in the env var and
// src/main/resources/application.properties, or src/main/resources/application.yaml
func (s SpringDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
// check if port is set on env var
// case: port is set on env var
ports := getSpringPortsFromEnvs()
if len(ports) > 0 {
component.Ports = ports
return
}
applicationFile := utils.GetAnyApplicationFilePath(component.Path, []model.ApplicationFileInfo{
{
Dir: "src/main/resources",
File: "application.properties",
},
{
Dir: "src/main/resources",
File: "application.yml",
},
{
Dir: "src/main/resources",
File: "application.yaml",
},
}, ctx)
// check if port is set on env var of dockerfile
ports = getSpringPortsFromEnvDockerfile(component.Path)
if len(ports) > 0 {
component.Ports = ports
return
}
// check if port is set inside application file
appFileInfos := s.GetApplicationFileInfos(component.Path, ctx)
if len(appFileInfos) == 0 {
return
}
applicationFile := utils.GetAnyApplicationFilePath(component.Path, appFileInfos, ctx)
if applicationFile == "" {
return
}
var err error
if filepath.Ext(applicationFile) == ".yml" || filepath.Ext(applicationFile) == ".yaml" {
ports, err = getServerPortsFromYamlFile(applicationFile)
@@ -88,6 +103,15 @@ func getSpringPortsFromEnvs() []int {
return utils.GetValidPortsFromEnvs([]string{"SERVER_PORT", "SERVER_HTTP_PORT"})
}
func getSpringPortsFromEnvDockerfile(path string) []int {
envVars, err := utils.GetEnvVarsFromDockerFile(path)
if err != nil {
return nil
}
envs := []string{"SERVER_PORT", "SERVER_HTTP_PORT"}
return utils.GetValidPortsFromEnvDockerfile(envs, envVars)
}
func getServerPortsFromPropertiesFile(file string) ([]int, error) {
props, err := utils.ConvertPropertiesFileAsPathToMap(file)
if err != nil {
@@ -122,11 +146,11 @@ func getPortFromMap(props map[string]string, key string) int {
}
func getServerPortsFromYamlFile(file string) ([]int, error) {
yamlFile, err := ioutil.ReadFile(file)
yamlFile, err := os.ReadFile(filepath.Clean(file))
if err != nil {
return []int{}, err
}
var data ApplicationProsServer
var data model.SpringApplicationProsServer
err = yaml.Unmarshal(yamlFile, &data)
if err != nil {
return []int{}, err

View File

@@ -21,18 +21,21 @@ import (
type VertxDetector struct{}
type VertxConf struct {
Port int `json:"http.port,omitempty"`
ServerConfig ServerConfig `json:"http.server,omitempty"`
}
type ServerConfig struct {
Port int `json:"http.server.port,omitempty"`
}
func (v VertxDetector) GetSupportedFrameworks() []string {
return []string{"Vertx"}
}
func (v VertxDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
return []model.ApplicationFileInfo{
{
Context: ctx,
Root: componentPath,
Dir: "src/main/conf",
File: ".*.json",
},
}
}
// DoFrameworkDetection uses the groupId to check for the framework name
func (v VertxDetector) DoFrameworkDetection(language *model.Language, config string) {
if hasFwk, _ := hasFramework(config, "io.vertx", ""); hasFwk {
@@ -42,24 +45,31 @@ func (v VertxDetector) DoFrameworkDetection(language *model.Language, config str
// DoPortsDetection searches for the port in json files under src/main/conf/
func (v VertxDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
bytes, err := utils.ReadAnyApplicationFile(component.Path, []model.ApplicationFileInfo{
{
Dir: "src/main/conf",
File: ".*.json",
},
}, ctx)
if err != nil {
appFileInfos := v.GetApplicationFileInfos(component.Path, ctx)
if len(appFileInfos) == 0 {
return
}
var data VertxConf
err = json.Unmarshal(bytes, &data)
if err != nil {
return
}
if utils.IsValidPort(data.Port) {
component.Ports = []int{data.Port}
} else if utils.IsValidPort(data.ServerConfig.Port) {
component.Ports = []int{data.ServerConfig.Port}
}
for _, appFileInfo := range appFileInfos {
fileBytes, err := utils.GetApplicationFileBytes(appFileInfo)
if err != nil {
continue
}
var data model.VertxConf
err = json.Unmarshal(fileBytes, &data)
if err != nil {
continue
}
if utils.IsValidPort(data.Port) {
component.Ports = []int{data.Port}
return
}
if utils.IsValidPort(data.ServerConfig.Port) {
component.Ports = []int{data.ServerConfig.Port}
return
}
}
}

View File

@@ -13,19 +13,30 @@ package enricher
import (
"context"
"encoding/xml"
"github.com/devfile/alizer/pkg/apis/model"
"github.com/devfile/alizer/pkg/schema"
"github.com/devfile/alizer/pkg/utils"
)
type WildFlyDetector struct{}
func (o WildFlyDetector) GetSupportedFrameworks() []string {
func (w WildFlyDetector) GetSupportedFrameworks() []string {
return []string{"WildFly"}
}
func (w WildFlyDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
files, err := utils.GetCachedFilePathsFromRoot(componentPath, ctx)
if err != nil {
return []model.ApplicationFileInfo{}
}
pomXML := utils.GetFile(&files, "pom.xml")
return utils.GenerateApplicationFileFromFilters([]string{pomXML}, componentPath, "", ctx)
}
// DoFrameworkDetection uses the groupId and artifactId to check for the framework name
func (o WildFlyDetector) DoFrameworkDetection(language *model.Language, config string) {
func (w WildFlyDetector) DoFrameworkDetection(language *model.Language, config string) {
if hasFwk, _ := hasFramework(config, "org.wildfly.plugins", "wildfly-maven-plugin"); hasFwk {
language.Frameworks = append(language.Frameworks, "WildFly")
}
@@ -33,17 +44,29 @@ func (o WildFlyDetector) DoFrameworkDetection(language *model.Language, config s
// DoPortsDetection for wildfly fetches the pom.xml and tries to find any javaOpts under
// the wildfly-maven-plugin profiles. If there is one it looks if jboss.http.port is defined.
func (o WildFlyDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
func (w WildFlyDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
ports := []int{}
// Fetch the content of xml for this component
paths, err := utils.GetCachedFilePathsFromRoot(component.Path, ctx)
if err != nil {
appFileInfos := w.GetApplicationFileInfos(component.Path, ctx)
if len(appFileInfos) == 0 {
return
}
pomXML := utils.GetFile(&paths, "pom.xml")
portPlaceholder := GetPortsForJBossFrameworks(pomXML, "wildfly-maven-plugin", "org.wildfly.plugins")
for _, appFileInfo := range appFileInfos {
fileBytes, err := utils.GetApplicationFileBytes(appFileInfo)
if err != nil {
continue
}
var pom schema.Pom
err = xml.Unmarshal(fileBytes, &pom)
if err != nil {
continue
}
portPlaceholder := GetPortsForJBossFrameworks(pom, "wildfly-maven-plugin", "org.wildfly.plugins")
if portPlaceholder == "" {
return
continue
}
if port, err := utils.GetValidPort(portPlaceholder); err == nil {
@@ -55,3 +78,4 @@ func (o WildFlyDetector) DoPortsDetection(component *model.Component, ctx *conte
return
}
}
}

View File

@@ -19,35 +19,29 @@ import (
"github.com/devfile/alizer/pkg/utils"
)
type AngularCliJson struct {
Defaults struct {
Serve HostPort `json:"serve"`
} `json:"defaults"`
}
type AngularJson struct {
Projects map[string]ProjectBody `json:"projects"`
}
type ProjectBody struct {
Architect struct {
Serve struct {
Options HostPort `json:"options"`
} `json:"serve"`
} `json:"architect"`
}
type HostPort struct {
Host string `json:"host"`
Port int `json:"port"`
}
type AngularDetector struct{}
func (a AngularDetector) GetSupportedFrameworks() []string {
return []string{"Angular"}
}
func (a AngularDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
return []model.ApplicationFileInfo{
{
Context: ctx,
Root: componentPath,
Dir: "",
File: "angular.json",
},
{
Context: ctx,
Root: componentPath,
Dir: "",
File: "angular-cli.json",
},
}
}
// DoFrameworkDetection uses a tag to check for the framework name
func (a AngularDetector) DoFrameworkDetection(language *model.Language, config string) {
if hasFramework(config, "angular") {
@@ -58,17 +52,26 @@ func (a AngularDetector) DoFrameworkDetection(language *model.Language, config s
// DoPortsDetection searches for the port in angular.json, package.json, and angular-cli.json
func (a AngularDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
// check if port is set on angular.json file
bytes, err := utils.ReadAnyApplicationFile(component.Path, []model.ApplicationFileInfo{
{
Dir: "",
File: "angular.json",
},
}, ctx)
appFileInfos := a.GetApplicationFileInfos(component.Path, ctx)
if len(appFileInfos) == 0 {
return
}
appFileInfo, err := utils.GetApplicationFileInfo(appFileInfos, "angular.json")
if err != nil {
return
}
var data AngularJson
err = json.Unmarshal(bytes, &data)
fileBytes, err := utils.GetApplicationFileBytes(appFileInfo)
if err != nil {
return
}
if err != nil {
return
}
var data model.AngularJson
err = json.Unmarshal(fileBytes, &data)
if err != nil {
return
}
@@ -89,17 +92,18 @@ func (a AngularDetector) DoPortsDetection(component *model.Component, ctx *conte
}
// check if port is set on angular-cli.json file
bytes, err = utils.ReadAnyApplicationFile(component.Path, []model.ApplicationFileInfo{
{
Dir: "",
File: "angular-cli.json",
},
}, ctx)
appFileInfoCli, err := utils.GetApplicationFileInfo(appFileInfos, "angular-cli.json")
if err != nil {
return
}
var dataCli AngularCliJson
err = json.Unmarshal(bytes, &dataCli)
fileBytesCli, err := utils.GetApplicationFileBytes(appFileInfoCli)
if err != nil {
return
}
var dataCli model.AngularCliJson
err = json.Unmarshal(fileBytesCli, &dataCli)
if err != nil {
return
}

View File

@@ -14,7 +14,6 @@ package enricher
import (
"context"
"os"
"path/filepath"
"regexp"
"strings"
@@ -28,6 +27,14 @@ func (e ExpressDetector) GetSupportedFrameworks() []string {
return []string{"Express"}
}
func (e ExpressDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
files, err := utils.GetCachedFilePathsFromRoot(componentPath, ctx)
if err != nil {
return []model.ApplicationFileInfo{}
}
return utils.GenerateApplicationFileFromFilters(files, componentPath, ".js", ctx)
}
// DoFrameworkDetection uses a tag to check for the framework name
func (e ExpressDetector) DoFrameworkDetection(language *model.Language, config string) {
if hasFramework(config, "express") {
@@ -36,25 +43,19 @@ func (e ExpressDetector) DoFrameworkDetection(language *model.Language, config s
}
func (e ExpressDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
files, err := utils.GetCachedFilePathsFromRoot(component.Path, ctx)
fileContents, err := utils.GetApplicationFileContents(e.GetApplicationFileInfos(component.Path, ctx))
if err != nil {
return
}
re := regexp.MustCompile(`\.listen\([^,)]*`)
var ports []int
for _, file := range files {
cleanFile := filepath.Clean(file)
bytes, err := os.ReadFile(cleanFile)
if err != nil {
continue
}
content := string(bytes)
for _, content := range fileContents {
matchesIndexes := re.FindAllStringSubmatchIndex(content, -1)
for _, matchIndexes := range matchesIndexes {
port := getPort(content, matchIndexes)
if port != -1 {
ports = append(ports, port)
portList := getPorts(content, matchIndexes, component.Path)
if len(portList) != 0 {
ports = append(ports, portList...)
}
}
if len(ports) > 0 {
@@ -82,14 +83,39 @@ func GetEnvPort(envPlaceholder string) int {
return -1
}
func getPort(content string, matchIndexes []int) int {
// GetEnvPortFromDockerfile returns a port value from the Dockerfile (locations provided by the 'utils.GetLocations' function)
// matching the specified 'envPlaceHolder'.
// It first extracts the environment variable name to lookup from 'envPlaceholder' by removing any 'process.env.' prefix.
// It then searches through all environment variables detected from the Dockerfile (as determined by 'utils.GetEnvVarsFromDockerFile').
// And if the environment variable specified via 'envPlaceholder' is found in the Dockerfile environment variables
// and its corresponding value is a valid port (as determined by 'utils.GetValidPort'), it returns this valid port value.
//
// If there is an error reading the Dockerfile or if the environment variable specified via 'envPlaceholder' is not found among
// the Dockerfile environment variables, the function returns -1.
func GetEnvPortFromDockerfile(envPlaceholder string, path string) int {
envPlaceholder = strings.Replace(envPlaceholder, "process.env.", "", -1)
envVars, err := utils.GetEnvVarsFromDockerFile(path)
if err != nil {
return -1
}
for _, envVar := range envVars {
if envVar.Name == envPlaceholder {
if port, err := utils.GetValidPort(envVar.Value); err == nil {
return port
}
}
}
return -1
}
func getPorts(content string, matchIndexes []int, path string) []int {
// Express configures its port with app.listen()
portPlaceholder := content[matchIndexes[0]:matchIndexes[1]]
portPlaceholder = strings.Replace(portPlaceholder, ".listen(", "", -1)
// Case: Raw port value -> return it directly
if port, err := utils.GetValidPort(portPlaceholder); err == nil {
return port
return []int{port}
}
// Case: Env var given as value in app.listen -> Get env value
@@ -115,13 +141,20 @@ func getPort(content string, matchIndexes []int) int {
}
}
}
var result []int
// After double-checking for env vars try to get the value of this port
if len(envMatchIndexes) > 1 {
envPlaceholder := envPortValue[envMatchIndexes[0]:envMatchIndexes[1]]
port := GetEnvPort(envPlaceholder)
// The port will be return only if a value was found for the given env var
if port > 0 {
return port
result = append(result, port)
} else {
// If no env var was found on system try to find one in a root dockerfile
port = GetEnvPortFromDockerfile(envPlaceholder, path)
if port > 0 {
result = append(result, port)
}
}
}
// Case: No env var or raw value found -> check for raw value into a var
@@ -131,9 +164,10 @@ func getPort(content string, matchIndexes []int) int {
portValues := strings.Split(potentialPortGroup, " || ")
for _, portValue := range portValues {
if port, err := utils.GetValidPort(portValue); err == nil {
return port
result = append(result, port)
break
}
}
}
return -1
return result
}

View File

@@ -24,6 +24,12 @@ func (n NextDetector) GetSupportedFrameworks() []string {
return []string{"Next"}
}
func (a NextDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
// Next.js enricher does not apply source code detection.
// It only detects ports from start/dev script
return []model.ApplicationFileInfo{}
}
// DoFrameworkDetection uses a tag to check for the framework name
func (n NextDetector) DoFrameworkDetection(language *model.Language, config string) {
if hasFramework(config, "next") {

View File

@@ -25,6 +25,17 @@ func (n NuxtDetector) GetSupportedFrameworks() []string {
return []string{"Nuxt"}
}
func (n NuxtDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
return []model.ApplicationFileInfo{
{
Context: ctx,
Root: componentPath,
Dir: "",
File: "nuxt.config.js",
},
}
}
// DoFrameworkDetection uses a tag to check for the framework name
func (n NuxtDetector) DoFrameworkDetection(language *model.Language, config string) {
if hasFramework(config, "nuxt") {
@@ -34,6 +45,7 @@ func (n NuxtDetector) DoFrameworkDetection(language *model.Language, config stri
// DoPortsDetection searches for the port in package.json, and nuxt.config.js
func (n NuxtDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
ports := []int{}
regexes := []string{`--port=(\d*)`}
// check if port is set in start script in package.json
port := getPortFromStartScript(component.Path, regexes)
@@ -50,15 +62,22 @@ func (n NuxtDetector) DoPortsDetection(component *model.Component, ctx *context.
}
//check inside the nuxt.config.js file
bytes, err := utils.ReadAnyApplicationFile(component.Path, []model.ApplicationFileInfo{
{
Dir: "",
File: "nuxt.config.js",
},
}, ctx)
if err != nil {
appFileInfos := n.GetApplicationFileInfos(component.Path, ctx)
if len(appFileInfos) == 0 {
return
}
re := regexp.MustCompile(`port:\s*(\d+)*`)
component.Ports = utils.FindAllPortsSubmatch(re, string(bytes), 1)
for _, appFileInfo := range appFileInfos {
fileBytes, err := utils.GetApplicationFileBytes(appFileInfo)
if err != nil {
continue
}
re := regexp.MustCompile(`port:\s*(\d+)*`)
ports = utils.FindAllPortsSubmatch(re, string(fileBytes), 1)
if len(ports) > 0 {
component.Ports = ports
return
}
}
}

View File

@@ -25,6 +25,12 @@ func (r ReactJsDetector) GetSupportedFrameworks() []string {
return []string{"React"}
}
func (r ReactJsDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
// React.js enricher does not apply source code detection.
// It only detects ports from start script or env vars
return nil
}
// DoFrameworkDetection uses a tag to check for the framework name
func (r ReactJsDetector) DoFrameworkDetection(language *model.Language, config string) {
if hasFramework(config, "react") {
@@ -46,6 +52,14 @@ func (r ReactJsDetector) DoPortsDetection(component *model.Component, ctx *conte
component.Ports = []int{port}
return
}
// check if port is set on as env var inside a dockerfile
ports, err := utils.GetEnvVarPortValueFromDockerfile(component.Path, []string{"PORT"})
if err == nil {
component.Ports = ports
return
}
// check if port is set in start script in package.json
port = getPortFromStartScript(component.Path, []string{`PORT=(\d*)`})
if utils.IsValidPort(port) {

View File

@@ -24,6 +24,12 @@ func (s SvelteDetector) GetSupportedFrameworks() []string {
return []string{"Svelte"}
}
func (s SvelteDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
// Svelte.js enricher does not apply source code detection.
// It only detects ports from dev script
return nil
}
// DoFrameworkDetection uses a tag to check for the framework name
func (s SvelteDetector) DoFrameworkDetection(language *model.Language, config string) {
if hasFramework(config, "svelte") {

View File

@@ -25,6 +25,17 @@ func (v VueDetector) GetSupportedFrameworks() []string {
return []string{"Vue"}
}
func (v VueDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
return []model.ApplicationFileInfo{
{
Context: ctx,
Root: componentPath,
Dir: "",
File: "vue.config.js",
},
}
}
// DoFrameworkDetection uses a tag to check for the framework name
func (v VueDetector) DoFrameworkDetection(language *model.Language, config string) {
if hasFramework(config, "vue") {
@@ -35,6 +46,7 @@ func (v VueDetector) DoFrameworkDetection(language *model.Language, config strin
// DoPortsDetection searches for the port in package.json, .env file, and vue.config.js
func (v VueDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
regexes := []string{`--port (\d*)`, `PORT=(\d*)`}
ports := []int{}
// check if --port or PORT is set in start script in package.json
port := getPortFromStartScript(component.Path, regexes)
if utils.IsValidPort(port) {
@@ -54,16 +66,30 @@ func (v VueDetector) DoPortsDetection(component *model.Component, ctx *context.C
return
}
//check inside the vue.config.js file
bytes, err := utils.ReadAnyApplicationFile(component.Path, []model.ApplicationFileInfo{
{
Dir: "",
File: "vue.config.js",
},
}, ctx)
if err != nil {
// check if port is set on as env var inside a dockerfile
ports, err := utils.GetEnvVarPortValueFromDockerfile(component.Path, []string{"PORT"})
if err == nil {
component.Ports = ports
return
}
re := regexp.MustCompile(`port:\s*(\d+)*`)
component.Ports = utils.FindAllPortsSubmatch(re, string(bytes), 1)
//check inside the vue.config.js file
appFileInfos := v.GetApplicationFileInfos(component.Path, ctx)
if len(appFileInfos) == 0 {
return
}
for _, appFileInfo := range appFileInfos {
fileBytes, err := utils.GetApplicationFileBytes(appFileInfo)
if err != nil {
continue
}
re := regexp.MustCompile(`port:\s*(\d+)*`)
ports = utils.FindAllPortsSubmatch(re, string(fileBytes), 1)
if len(ports) > 0 {
component.Ports = ports
return
}
}
}

View File

@@ -13,6 +13,7 @@ package enricher
import (
"context"
"github.com/devfile/alizer/pkg/apis/model"
"github.com/devfile/alizer/pkg/utils"
)
@@ -23,6 +24,12 @@ func (d LaravelDetector) GetSupportedFrameworks() []string {
return []string{"Laravel"}
}
func (d LaravelDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
// laravel enricher does not apply source code detection.
// It only detects ports declared as env vars
return nil
}
// DoFrameworkDetection uses a tag to check for the framework name
func (d LaravelDetector) DoFrameworkDetection(language *model.Language, config string) {
if hasFramework(config, "laravel") {
@@ -34,8 +41,17 @@ func (d LaravelDetector) DoFrameworkDetection(language *model.Language, config s
// configuring the APP_PORT variable which is dedicated to port configuration.
func (d LaravelDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
regexes := []string{`APP_PORT=(\d*)`}
// Case ENV file
ports := utils.GetPortValuesFromEnvFile(component.Path, regexes)
if len(ports) > 0 {
component.Ports = ports
return
}
// Case env var defined inside dockerfile
ports, err := utils.GetEnvVarPortValueFromDockerfile(component.Path, []string{"APP_PORT"})
if len(ports) > 0 && err != nil {
component.Ports = ports
return
}
}

View File

@@ -25,46 +25,66 @@ func (d DjangoDetector) GetSupportedFrameworks() []string {
return []string{"Django"}
}
func (d DjangoDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
return []model.ApplicationFileInfo{
{
Context: ctx,
Root: componentPath,
Dir: "",
File: "manage.py",
},
}
}
func (d DjangoDetector) GetDjangoFilenames() []string {
return []string{"manage.py", "urls.py", "wsgi.py", "asgi.py"}
}
func (d DjangoDetector) GetConfigDjangoFilenames() []string {
return []string{"requirements.txt", "pyproject.toml"}
}
// DoFrameworkDetection uses a tag to check for the framework name
// with django files and django config files
func (d DjangoDetector) DoFrameworkDetection(language *model.Language, files *[]string) {
managePy := utils.GetFile(files, "manage.py")
urlsPy := utils.GetFile(files, "urls.py")
wsgiPy := utils.GetFile(files, "wsgi.py")
asgiPy := utils.GetFile(files, "asgi.py")
requirementsTxt := utils.GetFile(files, "requirements.txt")
projectToml := utils.GetFile(files, "pyproject.toml")
var djangoFiles []string
var configDjangoFiles []string
utils.AddToArrayIfValueExist(&djangoFiles, managePy)
utils.AddToArrayIfValueExist(&djangoFiles, urlsPy)
utils.AddToArrayIfValueExist(&djangoFiles, wsgiPy)
utils.AddToArrayIfValueExist(&djangoFiles, asgiPy)
utils.AddToArrayIfValueExist(&configDjangoFiles, requirementsTxt)
utils.AddToArrayIfValueExist(&configDjangoFiles, projectToml)
for _, filename := range d.GetDjangoFilenames() {
filePy := utils.GetFile(files, filename)
utils.AddToArrayIfValueExist(&djangoFiles, filePy)
}
for _, filename := range d.GetConfigDjangoFilenames() {
configFile := utils.GetFile(files, filename)
utils.AddToArrayIfValueExist(&configDjangoFiles, configFile)
}
if hasFramework(&djangoFiles, "from django.") || hasFramework(&configDjangoFiles, "django") || hasFramework(&configDjangoFiles, "Django") {
language.Frameworks = append(language.Frameworks, "Django")
}
}
type ApplicationPropertiesFile struct {
Dir string
File string
}
// DoPortsDetection searches for the port in /manage.py
func (d DjangoDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
bytes, err := utils.ReadAnyApplicationFile(component.Path, []model.ApplicationFileInfo{
{
Dir: "",
File: "manage.py",
},
}, ctx)
if err != nil {
ports := []int{}
appFileInfos := d.GetApplicationFileInfos(component.Path, ctx)
if len(appFileInfos) == 0 {
return
}
re := regexp.MustCompile(`.default_port\s*=\s*"([^"]*)`)
component.Ports = utils.FindAllPortsSubmatch(re, string(bytes), 1)
for _, appFileInfo := range appFileInfos {
fileBytes, err := utils.GetApplicationFileBytes(appFileInfo)
if err != nil {
continue
}
re := regexp.MustCompile(`.default_port\s*=\s*"([^"]*)`)
component.Ports = utils.FindAllPortsSubmatch(re, string(fileBytes), 1)
if len(ports) > 0 {
component.Ports = ports
return
}
}
}

View File

@@ -22,30 +22,92 @@ import (
type FlaskDetector struct{}
func (d FlaskDetector) GetSupportedFrameworks() []string {
func (f FlaskDetector) GetSupportedFrameworks() []string {
return []string{"Flask"}
}
func (f FlaskDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
return []model.ApplicationFileInfo{
{
Context: ctx,
Root: componentPath,
Dir: "",
File: "app.py",
},
{
Context: ctx,
Root: componentPath,
Dir: "",
File: "wsgi.py",
},
{
Context: ctx,
Root: componentPath,
Dir: "app",
File: "__init__.py",
},
}
}
func (f FlaskDetector) GetFlaskFilenames() []string {
return []string{"app.py", "wsgi.py"}
}
func (f FlaskDetector) GetConfigFlaskFilenames() []string {
return []string{"requirements.txt", "pyproject.toml"}
}
// DoFrameworkDetection uses a tag to check for the framework name
// with flask files and flask config files
func (d FlaskDetector) DoFrameworkDetection(language *model.Language, files *[]string) {
appPy := utils.GetFile(files, "app.py")
wsgiPy := utils.GetFile(files, "wsgi.py")
requirementsTxt := utils.GetFile(files, "requirements.txt")
projectToml := utils.GetFile(files, "pyproject.toml")
func (f FlaskDetector) DoFrameworkDetection(language *model.Language, files *[]string) {
var flaskFiles []string
var configFlaskFiles []string
flaskFiles := []string{}
configFlaskFiles := []string{}
utils.AddToArrayIfValueExist(&flaskFiles, appPy)
utils.AddToArrayIfValueExist(&flaskFiles, wsgiPy)
utils.AddToArrayIfValueExist(&configFlaskFiles, requirementsTxt)
utils.AddToArrayIfValueExist(&configFlaskFiles, projectToml)
for _, filename := range f.GetFlaskFilenames() {
filePy := utils.GetFile(files, filename)
utils.AddToArrayIfValueExist(&flaskFiles, filePy)
}
for _, filename := range f.GetConfigFlaskFilenames() {
configFile := utils.GetFile(files, filename)
utils.AddToArrayIfValueExist(&configFlaskFiles, configFile)
}
if hasFramework(&flaskFiles, "from flask ") || hasFramework(&configFlaskFiles, "Flask") || hasFramework(&configFlaskFiles, "flask") {
language.Frameworks = append(language.Frameworks, "Flask")
}
}
// DoPortsDetection searches for the port in app/__init__.py, app.py or /wsgi.py
func (f FlaskDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
appFileInfos := f.GetApplicationFileInfos(component.Path, ctx)
if len(appFileInfos) == 0 {
return
}
for _, appFileInfo := range appFileInfos {
fileBytes, err := utils.GetApplicationFileBytes(appFileInfo)
if err != nil {
continue
}
matchIndexRegexes := []model.PortMatchRule{
{
Regex: regexp.MustCompile(`.run\([^)]*`),
ToReplace: ".run(",
},
}
if err != nil {
continue
}
ports := getPortFromFileFlask(matchIndexRegexes, string(fileBytes))
if len(ports) > 0 {
component.Ports = ports
return
}
}
}
// getPortFromFileFlask tries to find a port configuration inside a given file content
func getPortFromFileFlask(matchIndexRegexes []model.PortMatchRule, text string) []int {
var ports []int
@@ -99,36 +161,3 @@ func getPortWithMatchIndexesFlask(content string, matchIndexes []int, toBeReplac
return -1
}
// DoPortsDetection searches for the port in app/__init__.py, app.py or /wsgi.py
func (d FlaskDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
bytes, err := utils.ReadAnyApplicationFile(component.Path, []model.ApplicationFileInfo{
{
Dir: "",
File: "app.py",
},
{
Dir: "",
File: "wsgi.py",
},
{
Dir: "app",
File: "__init__.py",
},
}, ctx)
matchIndexRegexes := []model.PortMatchRule{
{
Regex: regexp.MustCompile(`.run\([^)]*`),
ToReplace: ".run(",
},
}
if err != nil {
return
}
ports := getPortFromFileFlask(matchIndexRegexes, string(bytes))
if len(ports) > 0 {
component.Ports = ports
return
}
}

View File

@@ -14,12 +14,12 @@ package enricher
import (
"context"
"errors"
"io/ioutil"
framework "github.com/devfile/alizer/pkg/apis/enricher/framework/go"
"github.com/devfile/alizer/pkg/apis/model"
"github.com/devfile/alizer/pkg/utils"
"golang.org/x/mod/modfile"
"os"
"path/filepath"
)
type GoEnricher struct{}
@@ -108,7 +108,7 @@ func (g GoEnricher) IsConfigValidForComponentDetection(language string, config s
}
func getGoModFile(filePath string) (*modfile.File, error) {
b, err := ioutil.ReadFile(filePath)
b, err := os.ReadFile(filepath.Clean(filePath))
if err != nil {
return nil, errors.New("unable to read go.mod file")
}

View File

@@ -11,9 +11,10 @@
package model
import "regexp"
type PortDetectionAlgorithm int
import (
"context"
"regexp"
)
const (
DockerFile PortDetectionAlgorithm = 0
@@ -21,66 +22,242 @@ const (
Source PortDetectionAlgorithm = 2
)
type DetectionSettings struct {
BasePath string
PortDetectionStrategy []PortDetectionAlgorithm
// All models inside model.go are sorted by name A-Z
// AngularCliJson represents the angular-cli.json file
type AngularCliJson struct {
Defaults struct {
Serve AngularHostPort `json:"serve"`
} `json:"defaults"`
}
type Language struct {
Name string
Aliases []string
Weight float64
Frameworks []string
Tools []string
CanBeComponent bool
// AngularHostPort represents the value of AngularCliJson.Defaults.Serve
type AngularHostPort struct {
Host string `json:"host"`
Port int `json:"port"`
}
type Component struct {
Name string
Path string
Languages []Language
Ports []int
// AngularJson represents angular.json
type AngularJson struct {
Projects map[string]AngularProjectBody `json:"projects"`
}
type Version struct {
SchemaVersion string
Default bool
Version string
}
type DevFileType struct {
Name string
Language string
ProjectType string
Tags []string
}
type DevfileFilter struct {
MinVersion string
MaxVersion string
// AngularProjectBody represents the value of each key of the map for AngularJson.Projects
type AngularProjectBody struct {
Architect struct {
Serve struct {
Options AngularHostPort `json:"options"`
} `json:"serve"`
} `json:"architect"`
}
// ApplicationFileInfo is the main struct used to select potential application files
// for detectors
type ApplicationFileInfo struct {
// Context is the given context
Context *context.Context
// Root is the root path of the component
Root string
// Dir is the directory of the application file
Dir string
// File is the filename of the application file
File string
}
type PortMatchRules struct {
MatchIndexRegexes []PortMatchRule
MatchRegexes []PortMatchSubRule
// Component represents every component detected from analysis process
type Component struct {
// Name is the name of the component
Name string
// Path is the root path of the component
Path string
// Languages is the slice of languages detected inside the component
Languages []Language
// Ports is the slice of integers (port values) detected
Ports []int
}
// DetectionSettings represents the required settings for component detection
type DetectionSettings struct {
// BasePath is the root path we need to apply detection process
BasePath string
// PortDetectionStrategy is the list of areas that we will apply port detection
// Accepted values can be found at PortDetectionAlgorithm
PortDetectionStrategy []PortDetectionAlgorithm
}
// DevfileFilter represents all filters passed to registry api upon requests
type DevfileFilter struct {
// MinSchemaVersion is the minimum schemaVersion of the fetched devfiles
MinSchemaVersion string
// MaxSchemaVersion is the maximum schemaVersion of the fetched devfiles
MaxSchemaVersion string
}
// DevfileScore represents the score that each devfile gets upon devfile matching process
type DevfileScore struct {
// DevfileIndex is the index of the fetched registry stacks slice
DevfileIndex int
// Score is the score that a devfile has. The biggest score gets matched with a given source code
Score int
}
// DevfileType represents a devfile.y(a)ml file
type DevfileType struct {
// Name is the name of a devfile
Name string
// Language is the language of a devfile
Language string
// ProjectType is the projectType of a devfile
ProjectType string
// Tags is a slice of tags of a devfile
Tags []string
// Versions is a slice of versions of a devfile
Versions []Version
}
// EnvVar represents an environment variable with a name and a corresponding value.
type EnvVar struct {
// Name is the name of the environment variable.
Name string
// Value is the value associated with the environment variable.
Value string
}
// Language represents every language detected from language analysis process
type Language struct {
// Name is the name of the language
Name string
// Aliases is the slice of aliases for this language
Aliases []string
// Weight is the float value which shows the importance of this language inside a given source code
Weight float64
// Frameworks is the slice of frameworks detected for this language
Frameworks []string
// Tools is the slice of tools detected for this language
Tools []string
// CanBeComponent is the bool value shows if this language can be detected as component
CanBeComponent bool
// CanBeContainerComponent is the bool value shows if this language can be detected as container component
CanBeContainerComponent bool
}
// MicronautApplicationProps represents the application.properties file of micronaut applications
type MicronautApplicationProps struct {
Micronaut struct {
Server struct {
Port int `yaml:"port,omitempty"`
SSL struct {
Enabled bool `yaml:"enabled,omitempty"`
Port int `yaml:"port,omitempty"`
} `yaml:"ssl,omitempty"`
} `yaml:"server,omitempty"`
} `yaml:"micronaut,omitempty"`
}
// OpenLibertyServerXml represents the server.xml file inside an open liberty application
type OpenLibertyServerXml struct {
HttpEndpoint struct {
HttpPort string `xml:"httpPort,attr"`
HttpsPort string `xml:"httpsPort,attr"`
} `xml:"httpEndpoint"`
}
// PortDetectionAlgorithm represents one of port detection algorithm values
type PortDetectionAlgorithm int
// PortMatchRule represents a rule for port matching with a given regex and a string to replace
type PortMatchRule struct {
// Regex is the regexp.Regexp value which will be used to match ports
Regex *regexp.Regexp
// ToReplace is the string value which will be replaced once the Regex is matched
ToReplace string
}
// PortMatchRules represents a struct of rules and subrules for port matching
type PortMatchRules struct {
// MatchIndexRegexes is a slice of PortMatchRule
MatchIndexRegexes []PortMatchRule
// MatchRegexes is a slice of PortMatchSubRule
MatchRegexes []PortMatchSubRule
}
// PortMatchSubRule represents a sub rule for port matching
type PortMatchSubRule struct {
// Regex is the primary regexp.Regexp value for the sub rule
Regex *regexp.Regexp
// Regex is the secondary regexp.Regexp value for the sub rule
SubRegex *regexp.Regexp
}
type DevFileScore struct {
DevFileIndex int
Score int
// QuarkusApplicationYaml represents the application.yaml used for quarkus applications
type QuarkusApplicationYaml struct {
Quarkus QuarkusHttp `yaml:"quarkus,omitempty"`
}
// QuarkusHttp represents the port field from application.yaml of quarkus applications
type QuarkusHttp struct {
Http QuarkusHttpPort `yaml:"http,omitempty"`
}
// QuarkusHttpPort represents the port value from application.yaml of quarkus applications
type QuarkusHttpPort struct {
Port int `yaml:"port,omitempty"`
InsecureRequests string `yaml:"insecure-requests,omitempty"`
SSLPort int `yaml:"ssl-port,omitempty"`
}
// SpringApplicationProsServer represents the application.properties file used for spring applications
type SpringApplicationProsServer struct {
Server struct {
Port int `yaml:"port,omitempty"`
Http struct {
Port int `yaml:"port,omitempty"`
} `yaml:"http,omitempty"`
} `yaml:"server,omitempty"`
}
// Version represents a version of a devfile
type Version struct {
// SchemaVersion is the schemaVersion value of a devfile version
SchemaVersion string
// Default is the default value of a devfile version
Default bool
// Version is the version tag of a devfile version
Version string
}
// VertxConf represents the config file for vertx applications
type VertxConf struct {
Port int `json:"http.port,omitempty"`
ServerConfig VertexServerConfig `json:"http.server,omitempty"`
}
// VertexServerConfig represents the server config file for vertx applications
type VertexServerConfig struct {
Port int `json:"http.server.port,omitempty"`
}

View File

@@ -38,6 +38,11 @@ func DetectComponents(path string) ([]model.Component, error) {
return detectComponentsWithPathAndPortStartegy(path, []model.PortDetectionAlgorithm{model.DockerFile, model.Compose, model.Source}, &ctx)
}
func DetectComponentsWithoutPortDetection(path string) ([]model.Component, error) {
ctx := context.Background()
return detectComponentsWithPathAndPortStartegy(path, []model.PortDetectionAlgorithm{}, &ctx)
}
func DetectComponentsInRootWithPathAndPortStartegy(path string, portDetectionStrategy []model.PortDetectionAlgorithm) ([]model.Component, error) {
ctx := context.Background()
return detectComponentsInRootWithPathAndPortStartegy(path, portDetectionStrategy, &ctx)
@@ -182,6 +187,17 @@ func isAnyComponentInPath(path string, components []model.Component) bool {
return false
}
// isAnyComponentInDirectPath checks if a component is present in the exact path.
// Search starts from path and will return true if a component is found.
func isAnyComponentInDirectPath(path string, components []model.Component) bool {
for _, component := range components {
if strings.Contains(path, component.Path) {
return true
}
}
return false
}
// isFirstPathParentOfSecond check if first path is parent (direct or not) of second path.
func isFirstPathParentOfSecond(firstPath string, secondPath string) bool {
return strings.Contains(secondPath, firstPath)
@@ -194,6 +210,7 @@ func DetectComponentsFromFilesList(files []string, settings model.DetectionSetti
alizerLogger.V(0).Info(fmt.Sprintf("Detecting components for %d fetched file paths", len(files)))
configurationPerLanguage := langfiles.Get().GetConfigurationPerLanguageMapping()
var components []model.Component
var containerComponents []model.Component
for _, file := range files {
alizerLogger.V(1).Info(fmt.Sprintf("Accessing %s", file))
languages, err := getLanguagesByConfigurationFile(configurationPerLanguage, file)
@@ -210,9 +227,21 @@ func DetectComponentsFromFilesList(files []string, settings model.DetectionSetti
alizerLogger.V(1).Info(err.Error())
continue
}
if component.Languages[0].CanBeComponent {
alizerLogger.V(0).Info(fmt.Sprintf("Component %s found", component.Name))
components = appendIfMissing(components, component)
}
if component.Languages[0].CanBeContainerComponent {
alizerLogger.V(0).Info(fmt.Sprintf("Container component %s found", component.Name))
containerComponents = appendIfMissing(containerComponents, component)
}
}
for _, component := range containerComponents {
if !isAnyComponentInDirectPath(component.Path, components) {
components = appendIfMissing(components, component)
}
}
return components
}
@@ -226,8 +255,9 @@ func appendIfMissing(components []model.Component, component model.Component) []
}
func getLanguagesByConfigurationFile(configurationPerLanguage map[string][]string, file string) ([]string, error) {
filename := filepath.Base(file)
for regex, languages := range configurationPerLanguage {
if match, _ := regexp.MatchString(regex, file); match {
if match, _ := regexp.MatchString(regex, filename); match {
return languages, nil
}
}

View File

@@ -16,7 +16,7 @@ import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"io"
"net/http"
"regexp"
"strings"
@@ -28,192 +28,219 @@ import (
const MinimumAllowedVersion = "2.0.0"
func SelectDevFilesFromTypes(path string, devFileTypes []model.DevFileType) ([]int, error) {
// DEPRECATION WARNING: This function is deprecated, please use devfile_recognizer.MatchDevfiles
// instead.
// func SelectDevFilesFromTypes: Returns a list of devfiles matched for the given application
func SelectDevFilesFromTypes(path string, devfileTypes []model.DevfileType) ([]int, error) {
alizerLogger := utils.GetOrCreateLogger()
ctx := context.Background()
alizerLogger.V(0).Info("Applying component detection to match a devfile")
devFilesIndexes := selectDevFilesFromComponentsDetectedInPath(path, devFileTypes)
if len(devFilesIndexes) > 0 {
alizerLogger.V(0).Info(fmt.Sprintf("Found %d potential matches", len(devFilesIndexes)))
return devFilesIndexes, nil
devfilesIndexes := selectDevfilesFromComponentsDetectedInPath(path, devfileTypes)
if len(devfilesIndexes) > 0 {
alizerLogger.V(0).Info(fmt.Sprintf("Found %d potential matches", len(devfilesIndexes)))
return devfilesIndexes, nil
}
alizerLogger.V(0).Info("No components found, applying language analysis for devfile matching")
languages, err := analyze(path, &ctx)
if err != nil {
return []int{}, err
}
devfile, err := SelectDevFileUsingLanguagesFromTypes(languages, devFileTypes)
mainLanguage, err := getMainLanguage(languages)
if err != nil {
return []int{}, err
}
devfiles, err := selectDevfilesByLanguage(mainLanguage, devfileTypes)
if err != nil {
return []int{}, errors.New("No valid devfile found for project in " + path)
}
return []int{devfile}, nil
return devfiles, nil
}
func selectDevFilesFromComponentsDetectedInPath(path string, devFileTypes []model.DevFileType) []int {
func getMainLanguage(languages []model.Language) (model.Language, error) {
if len(languages) == 0 {
return model.Language{}, fmt.Errorf("cannot detect main language due to empty languages list")
}
mainLanguage := languages[0]
for _, language := range languages {
if language.Weight > mainLanguage.Weight {
mainLanguage = language
}
}
return mainLanguage, nil
}
func selectDevfilesFromComponentsDetectedInPath(path string, devfileTypes []model.DevfileType) []int {
components, _ := DetectComponentsInRoot(path)
devFilesIndexes := selectDevFilesFromComponents(components, devFileTypes)
if len(devFilesIndexes) > 0 {
return devFilesIndexes
devfilesIndexes := selectDevfilesFromComponents(components, devfileTypes)
if len(devfilesIndexes) > 0 {
return devfilesIndexes
}
components, _ = DetectComponents(path)
return selectDevFilesFromComponents(components, devFileTypes)
return selectDevfilesFromComponents(components, devfileTypes)
}
func selectDevFilesFromComponents(components []model.Component, devFileTypes []model.DevFileType) []int {
var devFilesIndexes []int
func selectDevfilesFromComponents(components []model.Component, devfileTypes []model.DevfileType) []int {
var devfilesIndexes []int
for _, component := range components {
devFiles, err := selectDevFilesByLanguage(component.Languages[0], devFileTypes)
devfiles, err := selectDevfilesByLanguage(component.Languages[0], devfileTypes)
if err == nil {
devFilesIndexes = append(devFilesIndexes, devFiles...)
devfilesIndexes = append(devfilesIndexes, devfiles...)
}
}
return devFilesIndexes
return devfilesIndexes
}
func SelectDevFileFromTypes(path string, devFileTypes []model.DevFileType) (int, error) {
devfiles, err := SelectDevFilesFromTypes(path, devFileTypes)
// DEPRECATION WARNING: This function is deprecated, please use devfile_recognizer.MatchDevfiles
// instead.
// func SelectDevFileFromTypes: Returns the first devfile from the list of devfiles returned
// from SelectDevFilesFromTypes func. It also returns an error if exists.
func SelectDevFileFromTypes(path string, devfileTypes []model.DevfileType) (int, error) {
devfiles, err := SelectDevFilesFromTypes(path, devfileTypes)
if err != nil {
return -1, err
}
return devfiles[0], nil
}
func SelectDevFilesUsingLanguagesFromTypes(languages []model.Language, devFileTypes []model.DevFileType) ([]int, error) {
var devFilesIndexes []int
func SelectDevfilesUsingLanguagesFromTypes(languages []model.Language, devfileTypes []model.DevfileType) ([]int, error) {
var devfilesIndexes []int
alizerLogger := utils.GetOrCreateLogger()
alizerLogger.V(1).Info("Searching potential matches from detected languages")
for _, language := range languages {
alizerLogger.V(1).Info(fmt.Sprintf("Accessing %s language", language.Name))
devFiles, err := selectDevFilesByLanguage(language, devFileTypes)
devfiles, err := selectDevfilesByLanguage(language, devfileTypes)
if err == nil {
alizerLogger.V(1).Info(fmt.Sprintf("Found %d potential matches for language %s", len(devFiles), language.Name))
devFilesIndexes = append(devFilesIndexes, devFiles...)
alizerLogger.V(1).Info(fmt.Sprintf("Found %d potential matches for language %s", len(devfiles), language.Name))
devfilesIndexes = append(devfilesIndexes, devfiles...)
}
}
if len(devFilesIndexes) > 0 {
return devFilesIndexes, nil
if len(devfilesIndexes) > 0 {
return devfilesIndexes, nil
}
return []int{}, errors.New("no valid devfile found by using those languages")
}
func SelectDevFileUsingLanguagesFromTypes(languages []model.Language, devFileTypes []model.DevFileType) (int, error) {
devFilesIndexes, err := SelectDevFilesUsingLanguagesFromTypes(languages, devFileTypes)
func SelectDevfileUsingLanguagesFromTypes(languages []model.Language, devfileTypes []model.DevfileType) (int, error) {
devfilesIndexes, err := SelectDevfilesUsingLanguagesFromTypes(languages, devfileTypes)
if err != nil {
return -1, err
}
return devFilesIndexes[0], nil
return devfilesIndexes[0], nil
}
func MatchDevfiles(path string, url string, filter model.DevfileFilter) ([]model.DevFileType, error) {
func MatchDevfiles(path string, url string, filter model.DevfileFilter) ([]model.DevfileType, error) {
alizerLogger := utils.GetOrCreateLogger()
alizerLogger.V(0).Info("Starting devfile matching")
alizerLogger.V(1).Info(fmt.Sprintf("Downloading devfiles from registry %s", url))
devFileTypesFromRegistry, err := downloadDevFileTypesFromRegistry(url, filter)
devfileTypesFromRegistry, err := DownloadDevfileTypesFromRegistry(url, filter)
if err != nil {
return []model.DevFileType{}, err
return []model.DevfileType{}, err
}
return selectDevfiles(path, devFileTypesFromRegistry)
return selectDevfiles(path, devfileTypesFromRegistry)
}
func SelectDevFilesFromRegistry(path string, url string) ([]model.DevFileType, error) {
func SelectDevfilesFromRegistry(path string, url string) ([]model.DevfileType, error) {
alizerLogger := utils.GetOrCreateLogger()
alizerLogger.V(0).Info("Starting devfile matching")
alizerLogger.V(1).Info(fmt.Sprintf("Downloading devfiles from registry %s", url))
devFileTypesFromRegistry, err := downloadDevFileTypesFromRegistry(url, model.DevfileFilter{MinVersion: "", MaxVersion: ""})
devfileTypesFromRegistry, err := DownloadDevfileTypesFromRegistry(url, model.DevfileFilter{MinSchemaVersion: "", MaxSchemaVersion: ""})
if err != nil {
return []model.DevFileType{}, err
return []model.DevfileType{}, err
}
return selectDevfiles(path, devFileTypesFromRegistry)
return selectDevfiles(path, devfileTypesFromRegistry)
}
func selectDevfiles(path string, devFileTypesFromRegistry []model.DevFileType) ([]model.DevFileType, error) {
indexes, err := SelectDevFilesFromTypes(path, devFileTypesFromRegistry)
// selectDevfiles is exposed as global var in the purpose of mocking tests
var selectDevfiles = func(path string, devfileTypesFromRegistry []model.DevfileType) ([]model.DevfileType, error) {
indexes, err := SelectDevFilesFromTypes(path, devfileTypesFromRegistry)
if err != nil {
return []model.DevFileType{}, err
return []model.DevfileType{}, err
}
var devFileTypes []model.DevFileType
var devfileTypes []model.DevfileType
for _, index := range indexes {
devFileTypes = append(devFileTypes, devFileTypesFromRegistry[index])
devfileTypes = append(devfileTypes, devfileTypesFromRegistry[index])
}
return devFileTypes, nil
return devfileTypes, nil
}
func SelectDevFileFromRegistry(path string, url string) (model.DevFileType, error) {
devFileTypes, err := downloadDevFileTypesFromRegistry(url, model.DevfileFilter{MinVersion: "", MaxVersion: ""})
func SelectDevfileFromRegistry(path string, url string) (model.DevfileType, error) {
devfileTypes, err := DownloadDevfileTypesFromRegistry(url, model.DevfileFilter{MinSchemaVersion: "", MaxSchemaVersion: ""})
if err != nil {
return model.DevFileType{}, err
return model.DevfileType{}, err
}
index, err := SelectDevFileFromTypes(path, devFileTypes)
index, err := SelectDevFileFromTypes(path, devfileTypes)
if err != nil {
return model.DevFileType{}, err
return model.DevfileType{}, err
}
return devFileTypes[index], nil
return devfileTypes[index], nil
}
func GetUrlWithVersions(url, minVersion, maxVersion string) (string, error) {
func GetUrlWithVersions(url, minSchemaVersion, maxSchemaVersion string) (string, error) {
minAllowedVersion, err := version.NewVersion(MinimumAllowedVersion)
if err != nil {
return "", nil
}
if minVersion != "" && maxVersion != "" {
minV, err := version.NewVersion(minVersion)
if minSchemaVersion != "" && maxSchemaVersion != "" {
minV, err := version.NewVersion(minSchemaVersion)
if err != nil {
return url, nil
}
maxV, err := version.NewVersion(maxVersion)
maxV, err := version.NewVersion(maxSchemaVersion)
if err != nil {
return url, nil
}
if maxV.LessThan(minV) {
return "", fmt.Errorf("max-version cannot be lower than min-version")
return "", fmt.Errorf("max-schema-version cannot be lower than min-schema-version")
}
if maxV.LessThan(minAllowedVersion) || minV.LessThan(minAllowedVersion) {
return "", fmt.Errorf("min and/or max version are lower than the minimum allowed version (2.0.0)")
}
return fmt.Sprintf("%s?minSchemaVersion=%s&maxSchemaVersion=%s", url, minVersion, maxVersion), nil
} else if minVersion != "" {
minV, err := version.NewVersion(minVersion)
return fmt.Sprintf("%s?minSchemaVersion=%s&maxSchemaVersion=%s", url, minSchemaVersion, maxSchemaVersion), nil
} else if minSchemaVersion != "" {
minV, err := version.NewVersion(minSchemaVersion)
if err != nil {
return "", nil
}
if minV.LessThan(minAllowedVersion) {
return "", fmt.Errorf("min version is lower than the minimum allowed version (2.0.0)")
}
return fmt.Sprintf("%s?minSchemaVersion=%s", url, minVersion), nil
} else if maxVersion != "" {
maxV, err := version.NewVersion(maxVersion)
return fmt.Sprintf("%s?minSchemaVersion=%s", url, minSchemaVersion), nil
} else if maxSchemaVersion != "" {
maxV, err := version.NewVersion(maxSchemaVersion)
if err != nil {
return "", nil
}
if maxV.LessThan(minAllowedVersion) {
return "", fmt.Errorf("max version is lower than the minimum allowed version (2.0.0)")
}
return fmt.Sprintf("%s?maxSchemaVersion=%s", url, maxVersion), nil
return fmt.Sprintf("%s?maxSchemaVersion=%s", url, maxSchemaVersion), nil
} else {
return url, nil
}
}
func downloadDevFileTypesFromRegistry(url string, filter model.DevfileFilter) ([]model.DevFileType, error) {
// DownloadDevfileTypesFromRegistry is exposed as a global variable for the purpose of running mock tests
var DownloadDevfileTypesFromRegistry = func(url string, filter model.DevfileFilter) ([]model.DevfileType, error) {
url = adaptUrl(url)
tmpUrl := appendIndexPath(url)
url, err := GetUrlWithVersions(tmpUrl, filter.MinVersion, filter.MaxVersion)
url, err := GetUrlWithVersions(tmpUrl, filter.MinSchemaVersion, filter.MaxSchemaVersion)
if err != nil {
return nil, err
}
// This value is set by the user in order to configure the registry
resp, err := http.Get(url) // #nosec G107
if err != nil {
return []model.DevFileType{}, err
return []model.DevfileType{}, err
}
defer func() error {
if err := resp.Body.Close(); err != nil {
@@ -224,21 +251,21 @@ func downloadDevFileTypesFromRegistry(url string, filter model.DevfileFilter) ([
// Check server response
if resp.StatusCode != http.StatusOK {
return []model.DevFileType{}, errors.New("unable to fetch devfiles from the registry")
return []model.DevfileType{}, errors.New("unable to fetch devfiles from the registry")
}
body, err2 := ioutil.ReadAll(resp.Body)
body, err2 := io.ReadAll(resp.Body)
if err2 != nil {
return []model.DevFileType{}, errors.New("unable to fetch devfiles from the registry")
return []model.DevfileType{}, errors.New("unable to fetch devfiles from the registry")
}
var devFileTypes []model.DevFileType
err = json.Unmarshal(body, &devFileTypes)
var devfileTypes []model.DevfileType
err = json.Unmarshal(body, &devfileTypes)
if err != nil {
return []model.DevFileType{}, errors.New("unable to fetch devfiles from the registry")
return []model.DevfileType{}, errors.New("unable to fetch devfiles from the registry")
}
return devFileTypes, nil
return devfileTypes, nil
}
func appendIndexPath(url string) string {
@@ -261,7 +288,7 @@ func adaptUrl(url string) string {
return url
}
// selectDevFilesByLanguage detects devfiles that fit best with a project.
// selectDevfilesByLanguage detects devfiles that fit best with a project.
//
// Performs a search in two steps looping through all devfiles available.
// When a framework is detected, this is stored in a map but still not saved. A check is made eventually as there could be that a future or
@@ -270,21 +297,21 @@ func adaptUrl(url string) string {
//
// At the end, if some framework is supported by some devfile, they are returned. Otherwise, Alizer was not able to find any
// specific devfile for the frameworks detected and returned the devfiles which got the largest score.
func selectDevFilesByLanguage(language model.Language, devFileTypes []model.DevFileType) ([]int, error) {
var devFileIndexes []int
frameworkPerDevFile := make(map[string]model.DevFileScore)
func selectDevfilesByLanguage(language model.Language, devfileTypes []model.DevfileType) ([]int, error) {
var devfileIndexes []int
frameworkPerDevfile := make(map[string]model.DevfileScore)
scoreTarget := 0
for index, devFile := range devFileTypes {
for index, devfile := range devfileTypes {
score := 0
frameworkPerDevfileTmp := make(map[string]interface{})
if strings.EqualFold(devFile.Language, language.Name) || matches(language.Aliases, devFile.Language) != "" {
if strings.EqualFold(devfile.Language, language.Name) || matches(language.Aliases, devfile.Language) != "" {
score++
if frw := matchesFormatted(language.Frameworks, devFile.ProjectType); frw != "" {
if frw := matchesFormatted(language.Frameworks, devfile.ProjectType); frw != "" {
frameworkPerDevfileTmp[frw] = nil
score += utils.FRAMEWORK_WEIGHT
}
for _, tag := range devFile.Tags {
for _, tag := range devfile.Tags {
if frw := matchesFormatted(language.Frameworks, tag); frw != "" {
frameworkPerDevfileTmp[frw] = nil
score += utils.FRAMEWORK_WEIGHT
@@ -295,37 +322,37 @@ func selectDevFilesByLanguage(language model.Language, devFileTypes []model.DevF
}
for framework := range frameworkPerDevfileTmp {
devFileObj := frameworkPerDevFile[framework]
if score > devFileObj.Score {
frameworkPerDevFile[framework] = model.DevFileScore{
DevFileIndex: index,
devfileObj := frameworkPerDevfile[framework]
if score > devfileObj.Score {
frameworkPerDevfile[framework] = model.DevfileScore{
DevfileIndex: index,
Score: score,
}
}
}
if len(frameworkPerDevFile) == 0 {
if len(frameworkPerDevfile) == 0 {
if score == scoreTarget {
devFileIndexes = append(devFileIndexes, index)
devfileIndexes = append(devfileIndexes, index)
} else if score > scoreTarget {
scoreTarget = score
devFileIndexes = []int{index}
devfileIndexes = []int{index}
}
}
}
}
if len(frameworkPerDevFile) > 0 {
devFileIndexes = []int{}
for _, val := range frameworkPerDevFile {
devFileIndexes = append(devFileIndexes, val.DevFileIndex)
if len(frameworkPerDevfile) > 0 {
devfileIndexes = []int{}
for _, val := range frameworkPerDevfile {
devfileIndexes = append(devfileIndexes, val.DevfileIndex)
}
}
if len(devFileIndexes) == 0 {
return devFileIndexes, errors.New("No valid devfile found for current language " + language.Name)
if len(devfileIndexes) == 0 {
return devfileIndexes, errors.New("No valid devfile found for current language " + language.Name)
}
return devFileIndexes, nil
return devfileIndexes, nil
}
func matchesFormatted(values []string, valueToFind string) string {

View File

@@ -132,7 +132,9 @@ func AnalyzeFile(configFile string, targetLanguage string) (model.Language, erro
Frameworks: []string{},
Tools: []string{},
Weight: 100,
CanBeComponent: true}
CanBeComponent: lang.Component,
CanBeContainerComponent: lang.ContainerComponent,
}
langEnricher := enricher.GetEnricherByLanguage(targetLanguage)
if langEnricher != nil {
langEnricher.DoEnrichLanguage(&tmpLanguage, &[]string{configFile})

View File

@@ -0,0 +1,27 @@
/*******************************************************************************
* Copyright (c) 2023 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Red Hat, Inc.
******************************************************************************/
package schema
type DevfileYaml struct {
StarterProjects []struct {
Git struct {
CheckoutFrom struct {
Remote string `yaml:"remote"`
Revision string `yaml:"revision"`
} `yaml:"checkoutFrom"`
Remotes struct {
Origin string `yaml:"origin"`
} `yaml:"remotes"`
} `yaml:"git"`
SubDir string `yaml:"subDir"`
} `yaml:"starterProjects"`
}

View File

@@ -29,6 +29,7 @@ type LanguagesProperties map[string]LanguageProperties
type LanguageCustomization struct {
ConfigurationFiles []string `yaml:"configuration_files"`
Component bool `yaml:"component"`
ContainerComponent bool `yaml:"container_component"`
ExcludeFolders []string `yaml:"exclude_folders,omitempty"`
Aliases []string `yaml:"aliases"`
Disabled bool `default:"false" yaml:"disable_detection"`

View File

@@ -19,17 +19,18 @@ import (
"encoding/xml"
"errors"
"fmt"
"io/ioutil"
"io"
"io/fs"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"github.com/devfile/alizer/pkg/utils/langfiles"
"github.com/devfile/alizer/pkg/apis/model"
"github.com/devfile/alizer/pkg/schema"
"github.com/devfile/alizer/pkg/utils/langfiles"
"github.com/moby/buildkit/frontend/dockerfile/parser"
ignore "github.com/sabhiram/go-gitignore"
)
@@ -65,16 +66,6 @@ func GetFile(filePaths *[]string, wantedFile string) string {
return ""
}
// HasFile checks if the file is in a filePaths path.
func HasFile(files *[]string, wantedFile string) bool {
for _, path := range *files {
if IsPathOfWantedFile(path, wantedFile) {
return true
}
}
return false
}
// IsPathOfWantedFile checks if the file is in the path.
func IsPathOfWantedFile(path string, wantedFile string) bool {
_, file := filepath.Split(path)
@@ -83,7 +74,7 @@ func IsPathOfWantedFile(path string, wantedFile string) bool {
// IsTagInFile checks if the file contains the tag.
func IsTagInFile(file string, tag string) (bool, error) {
contentInByte, err := ioutil.ReadFile(file)
contentInByte, err := os.ReadFile(filepath.Clean(file))
if err != nil {
return false, err
}
@@ -143,7 +134,7 @@ func GetPomFileContent(pomFilePath string) (schema.Pom, error) {
if err != nil {
return schema.Pom{}, err
}
byteValue, _ := ioutil.ReadAll(xmlFile)
byteValue, _ := io.ReadAll(xmlFile)
var pom schema.Pom
err = xml.Unmarshal(byteValue, &pom)
@@ -298,7 +289,7 @@ func isFileInRoot(root string, file string) bool {
// GetFilePathsInRoot returns a slice of all files in the root.
func GetFilePathsInRoot(root string) ([]string, error) {
fileInfos, err := ioutil.ReadDir(root)
fileInfos, err := os.ReadDir(root)
if err != nil {
return nil, err
}
@@ -309,14 +300,16 @@ func GetFilePathsInRoot(root string) ([]string, error) {
return files, nil
}
// ConvertPropertiesFileAsPathToMap fetches a file from a given path and transforms it into a map
func ConvertPropertiesFileAsPathToMap(path string) (map[string]string, error) {
bytes, err := ioutil.ReadFile(path)
bytes, err := os.ReadFile(filepath.Clean(path))
if err != nil {
return nil, err
}
return ConvertPropertiesFileToMap(bytes)
}
// ConvertPropertiesFileAsPathToMap transforms a slice of bytes it into a map
func ConvertPropertiesFileToMap(fileInBytes []byte) (map[string]string, error) {
config := map[string]string{}
scanner := bufio.NewScanner(bytes.NewReader(fileInBytes))
@@ -352,6 +345,173 @@ func GetValidPortsFromEnvs(envs []string) []int {
return validPorts
}
// GetEnvVarsFromDockerFile returns a slice of env vars from Dockerfiles in the given directory.
func GetEnvVarsFromDockerFile(root string) ([]model.EnvVar, error) {
locations := GetLocations(root)
for _, location := range locations {
filePath := filepath.Join(root, location)
cleanFilePath := filepath.Clean(filePath)
file, err := os.Open(cleanFilePath)
if err == nil {
defer func() error {
if err := file.Close(); err != nil {
return fmt.Errorf("error closing file: %s", err)
}
return nil
}()
return readEnvVarsFromDockerfile(file)
}
}
return nil, fmt.Errorf("no dockefile found inside dir: %s", root)
}
// GetValidPortsFromEnvs returns a slice of valid ports from a dockerfile.
func GetValidPortsFromEnvDockerfile(envs []string, envVars []model.EnvVar) []int {
var validPorts []int
for _, env := range envs {
for _, envVar := range envVars {
if envVar.Name == env {
if port, err := GetValidPort(envVar.Value); err == nil {
validPorts = append(validPorts, port)
}
break
}
}
}
return validPorts
}
// GetLocations returns a list of file paths representing common locations where Dockerfiles might be found
// within the specified 'root' directory and one level down.
//
// It starts with a predefined list of common file names for a Dockerfile
// ('Dockerfile', 'Containerfile', 'dockerfile', 'containerfile'), and appends such file names to the 'root' subdirectories.
//
// Note that hidden files and directories (starting with a dot, e.g., '.git') are ignored while traversing the 'root' directory.
func GetLocations(root string) []string {
filenames := []string{"Dockerfile", "Containerfile", "dockerfile", "containerfile"}
locations := make([]string, len(filenames))
copy(locations, filenames)
entries, err := os.ReadDir(root)
if err != nil {
return locations
}
dirItems := make([]fs.FileInfo, 0, len(entries))
for _, entry := range entries {
info, err := entry.Info()
if err != nil {
return locations
}
dirItems = append(dirItems, info)
}
for _, item := range dirItems {
if strings.HasPrefix(item.Name(), ".") {
continue
}
tmpPath := filepath.Join(root, item.Name())
fileInfo, err := os.Stat(tmpPath)
if err != nil {
continue
}
if fileInfo.IsDir() {
for _, filename := range filenames {
locations = append(locations, filepath.Join(item.Name(), filename))
}
}
}
return locations
}
// ReadPortsFromDockerfile returns a slice of port numbers.
func ReadPortsFromDockerfile(file io.Reader) []int {
var ports []int
res, err := parser.Parse(file)
if err != nil {
return ports
}
for _, child := range res.AST.Children {
// check for the potential port number in a Dockerfile/Containerfile
if strings.ToLower(child.Value) == "expose" {
for n := child.Next; n != nil; n = n.Next {
if port, err := strconv.Atoi(n.Value); err == nil {
ports = append(ports, port)
}
}
}
}
return ports
}
func upsertEnvVar(envVars []model.EnvVar, envVar model.EnvVar) []model.EnvVar {
isPresent := false
for i := range envVars {
if envVars[i].Name == envVar.Name {
isPresent = true
envVars[i].Value = envVar.Value
}
}
if !isPresent {
envVars = append(envVars, envVar)
}
return envVars
}
// readEnvVarsFromDockerfile returns a slice of envVars.
func readEnvVarsFromDockerfile(file io.Reader) ([]model.EnvVar, error) {
var envVars []model.EnvVar
res, err := parser.Parse(file)
if err != nil {
return envVars, err
}
for _, child := range res.AST.Children {
// check for the potential env var in a Dockerfile/Containerfile
if strings.ToLower(child.Value) != "env" {
continue
}
firstNode := child.Next
var secondNode *parser.Node
if firstNode == nil {
continue
}
secondNode = firstNode.Next
if secondNode == nil {
continue
}
envVar := model.EnvVar{
Name: firstNode.Value,
Value: secondNode.Value,
}
envVars = upsertEnvVar(envVars, envVar)
}
return envVars, nil
}
// GetEnvVarPortValueFromDockerfile gets port value defined as env vars.
func GetEnvVarPortValueFromDockerfile(path string, portPlaceholders []string) ([]int, error) {
envVars, err := GetEnvVarsFromDockerFile(path)
ports := []int{}
if err != nil {
return ports, err
}
for _, envVar := range envVars {
for _, portPlaceholder := range portPlaceholders {
if envVar.Name != portPlaceholder {
continue
}
if port, err := GetValidPort(envVar.Value); err == nil {
ports = append(ports, port)
}
}
}
return ports, nil
}
// GetValidPorts returns a slice of valid ports.
func GetValidPorts(ports []string) []int {
var validPorts []int
@@ -407,9 +567,62 @@ func GetAnyApplicationFilePathExactMatch(root string, propsFiles []model.Applica
return ""
}
// ReadAnyApplicationFile returns a byte slice of a file if it exists in the directory and the given file name is a substring.
func ReadAnyApplicationFile(root string, propsFiles []model.ApplicationFileInfo, ctx *context.Context) ([]byte, error) {
return readAnyApplicationFile(root, propsFiles, false, ctx)
// GenerateApplicationFileFromFilters generates a slice of model.ApplicationFileInfo
// from a given list of files and the root path of a component. If suffix exists
// it generates items only for files ending with this suffix.
func GenerateApplicationFileFromFilters(files []string, path string, suffix string, ctx *context.Context) []model.ApplicationFileInfo {
applicationFileInfos := []model.ApplicationFileInfo{}
for _, file := range files {
if strings.HasSuffix(file, suffix) {
cleanPath := filepath.Clean(file)
filename := filepath.Base(cleanPath)
tmpDir := strings.ReplaceAll(file, path, "")
dir := strings.ReplaceAll(tmpDir, filename, "")
appFileInfo := model.ApplicationFileInfo{
Context: ctx,
Root: path,
Dir: dir,
File: filename,
}
applicationFileInfos = append(applicationFileInfos, appFileInfo)
}
}
return applicationFileInfos
}
// GetApplicationFileContents returns a slice of strings for all file contents found for a given
// slice of ApplicationFileInfo.
func GetApplicationFileContents(appFileInfos []model.ApplicationFileInfo) ([]string, error) {
fileContents := []string{}
for _, appFileInfo := range appFileInfos {
fileContent, err := GetApplicationFileBytes(appFileInfo)
if err == nil {
fileContents = append(fileContents, string(fileContent))
}
}
if len(fileContents) == 0 {
return fileContents, fmt.Errorf("error: no application file found matching given criteria")
}
return fileContents, nil
}
// GetApplicationFileBytes returns a slice of bytes of a file if it exists in the directory and the given file name is a substring.
func GetApplicationFileBytes(propsFile model.ApplicationFileInfo) ([]byte, error) {
bytes, err := readAnyApplicationFile(propsFile.Root, []model.ApplicationFileInfo{propsFile}, false, propsFile.Context)
if err != nil {
return bytes, fmt.Errorf("error: %s", err)
}
return bytes, nil
}
// GetApplicationFileInfo returns an item from a slice of applicationFileInfos if it matches the given filename
func GetApplicationFileInfo(propsFiles []model.ApplicationFileInfo, filename string) (model.ApplicationFileInfo, error) {
for _, propsFile := range propsFiles {
if propsFile.File == filename {
return propsFile, nil
}
}
return model.ApplicationFileInfo{}, fmt.Errorf("no ApplicationFileInfo found")
}
// ReadAnyApplicationFileExactMatch returns a byte slice if the exact given file exists in the directory.
@@ -426,11 +639,12 @@ func readAnyApplicationFile(root string, propsFiles []model.ApplicationFileInfo,
path = GetAnyApplicationFilePath(root, propsFiles, ctx)
}
if path != "" {
return ioutil.ReadFile(path)
return os.ReadFile(filepath.Clean(path))
}
return nil, errors.New("no file found")
}
// FindPortSubMatch returns a port number in case it finds one for a given regex group
func FindPortSubmatch(re *regexp.Regexp, text string, group int) int {
potentialPortGroup := FindPotentialPortGroup(re, text, group)
if potentialPortGroup != "" {
@@ -441,6 +655,7 @@ func FindPortSubmatch(re *regexp.Regexp, text string, group int) int {
return -1
}
// FindPotentialPortGroup returns a placeholder for port if is found
func FindPotentialPortGroup(re *regexp.Regexp, text string, group int) string {
if text != "" {
matches := re.FindStringSubmatch(text)
@@ -451,6 +666,7 @@ func FindPotentialPortGroup(re *regexp.Regexp, text string, group int) string {
return ""
}
// FindAllPortsSubmatch returns a slice of port int values, matching a regex inside a given text
func FindAllPortsSubmatch(re *regexp.Regexp, text string, group int) []int {
var ports []int
if text != "" {
@@ -467,6 +683,8 @@ func FindAllPortsSubmatch(re *regexp.Regexp, text string, group int) []int {
return ports
}
// GetPortValueFromEnvFile returns the first port value of a slice of port values
// declared from env var files.
func GetPortValueFromEnvFile(root string, regex string) int {
ports := GetPortValuesFromEnvFile(root, []string{regex})
if len(ports) > 0 {
@@ -475,6 +693,7 @@ func GetPortValueFromEnvFile(root string, regex string) int {
return -1
}
// GetPortValuesFromEnvFile returns all port values found inside an env var file
func GetPortValuesFromEnvFile(root string, regexes []string) []int {
var ports []int
text, err := getEnvFileContent(root)
@@ -494,6 +713,7 @@ func GetPortValuesFromEnvFile(root string, regexes []string) []int {
return ports
}
// GetStringValueFromEnvFile returns port values as string from env file
func GetStringValueFromEnvFile(root string, regex string) string {
text, err := getEnvFileContent(root)
if err != nil {
@@ -523,6 +743,7 @@ var getEnvFileContent = func(root string) (string, error) {
return string(bytes), nil
}
// NormalizeSplit splits a filepath into dir and filename
func NormalizeSplit(file string) (string, string) {
dir, fileName := filepath.Split(file)
if dir == "" {

View File

@@ -28,6 +28,7 @@ type LanguageItem struct {
ConfigurationFiles []string
ExcludeFolders []string
Component bool
ContainerComponent bool
disabled bool
}
@@ -87,6 +88,7 @@ func customizeLanguage(languageItem *LanguageItem) {
(*languageItem).ConfigurationFiles = customization.ConfigurationFiles
(*languageItem).ExcludeFolders = customization.ExcludeFolders
(*languageItem).Component = customization.Component
(*languageItem).ContainerComponent = customization.ContainerComponent
(*languageItem).Aliases = appendSlice((*languageItem).Aliases, customization.Aliases)
(*languageItem).disabled = customization.Disabled
}

View File

@@ -6,6 +6,13 @@ C#:
- ".*\\.\\w+proj"
- "appsettings.json"
component: true
Dockerfile:
aliases:
- "Containerfile"
configuration_files:
- "[Dd]ockerfile(\\.\\w+)?$"
- "[Cc]ontainerfile(\\.\\w+)?$"
container_component: true
F#:
aliases:
- "dotnet"
@@ -33,12 +40,12 @@ JavaScript:
exclude_folders:
- "node_modules"
configuration_files:
- "[^-]package.json"
- "package.json"
component: true
PHP:
configuration_files:
- "composer.json"
- "[^-]package.json"
- "package.json"
component: true
Python:
configuration_files:
@@ -55,7 +62,7 @@ TypeScript:
exclude_folders:
- "node_modules"
configuration_files:
- "[^-]package.json"
- "package.json"
component: true
Visual Basic .NET:
aliases:

2
vendor/modules.txt generated vendored
View File

@@ -150,7 +150,7 @@ github.com/danieljoos/wincred
# github.com/davecgh/go-spew v1.1.1
## explicit
github.com/davecgh/go-spew/spew
# github.com/devfile/alizer v1.0.1
# github.com/devfile/alizer v1.2.1
## explicit; go 1.19
github.com/devfile/alizer/pkg/apis/enricher
github.com/devfile/alizer/pkg/apis/enricher/framework/dotnet