mirror of
https://github.com/redhat-developer/odo.git
synced 2025-10-19 03:06:19 +03:00
Select and pull a devfile using Alizer (#5464)
* Add alizer library and test functionality <!-- Thank you for opening a PR! Here are some things you need to know before submitting: 1. Please read our developer guideline: https://github.com/redhat-developer/odo/wiki/Developer-Guidelines 2. Label this PR accordingly with the '/kind' line 3. Ensure you have written and ran the appropriate tests: https://github.com/redhat-developer/odo/wiki/Writing-and-running-tests 4. Read how we approve and LGTM each PR: https://github.com/redhat-developer/odo/wiki/PR-Review Documentation: If you are pushing a change to documentation, please read: https://github.com/redhat-developer/odo/wiki/Contributing-to-Docs --> **What type of PR is this:** <!-- Add one of the following kinds: /kind bug /kind cleanup /kind tests /kind documentation Feel free to use other [labels](https://github.com/redhat-developer/odo/labels) as needed. However one of the above labels must be present or the PR will not be reviewed. This instruction is for reviewers as well. --> /kind feature **What does this PR do / why we need it:** Adds the alizer library from https://github.com/redhat-developer/alizer/tree/main/go as part of our implementaion of `odo dev` and `odo init`. This builds upon @feloy 's PR located here: https://github.com/redhat-developer/odo/pull/5434 **Which issue(s) this PR fixes:** <!-- Specifying the issue will automatically close it when this PR is merged --> Fixes # **PR acceptance criteria:** - [X] Unit test - [X] Integration test - [X] Documentation **How to test changes / Special notes to the reviewer:** N/A. Only function implementation * New alizer version * Use alizer for odo init * Add integration tests * Add Alizer to odo deploy * review * Ask component name for odo deploy * Fix unit test Co-authored-by: Charlie Drage <charlie@charliedrage.com>
This commit is contained in:
1
go.mod
1
go.mod
@@ -35,6 +35,7 @@ require (
|
|||||||
github.com/pborman/uuid v1.2.0
|
github.com/pborman/uuid v1.2.0
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/posener/complete v1.1.1
|
github.com/posener/complete v1.1.1
|
||||||
|
github.com/redhat-developer/alizer/go v0.0.0-20220215154256-33df7feef4ae
|
||||||
github.com/redhat-developer/service-binding-operator v0.9.0
|
github.com/redhat-developer/service-binding-operator v0.9.0
|
||||||
github.com/securego/gosec/v2 v2.8.0
|
github.com/securego/gosec/v2 v2.8.0
|
||||||
github.com/segmentio/backo-go v0.0.0-20200129164019-23eae7c10bd3 // indirect
|
github.com/segmentio/backo-go v0.0.0-20200129164019-23eae7c10bd3 // indirect
|
||||||
|
|||||||
6
go.sum
6
go.sum
@@ -1007,6 +1007,10 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T
|
|||||||
github.com/pseudomuto/protoc-gen-doc v1.3.2/go.mod h1:y5+P6n3iGrbKG+9O04V5ld71in3v/bX88wUwgt+U8EA=
|
github.com/pseudomuto/protoc-gen-doc v1.3.2/go.mod h1:y5+P6n3iGrbKG+9O04V5ld71in3v/bX88wUwgt+U8EA=
|
||||||
github.com/pseudomuto/protokit v0.2.0/go.mod h1:2PdH30hxVHsup8KpBTOXTBeMVhJZVio3Q8ViKSAXT0Q=
|
github.com/pseudomuto/protokit v0.2.0/go.mod h1:2PdH30hxVHsup8KpBTOXTBeMVhJZVio3Q8ViKSAXT0Q=
|
||||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||||
|
github.com/redhat-developer/alizer/go v0.0.0-20220204121940-dec463ed3af3 h1:PFDkd6xGpa1SwWMj4bKrHc2xPAwA0x2PKZOlo1LfWYQ=
|
||||||
|
github.com/redhat-developer/alizer/go v0.0.0-20220204121940-dec463ed3af3/go.mod h1:18H8Trq+vkpxqek82e7SimdNTdFfOGGjN7gAUGDO3jU=
|
||||||
|
github.com/redhat-developer/alizer/go v0.0.0-20220215154256-33df7feef4ae h1:N2wsIYtziHQ51GNcJY5YcB0YldpR5BwPoTvby+l0vy8=
|
||||||
|
github.com/redhat-developer/alizer/go v0.0.0-20220215154256-33df7feef4ae/go.mod h1:EKkrP0Am7Xt/yg3dF8uH1SSoOcaZmBom8Iy6CJPPDok=
|
||||||
github.com/redhat-developer/service-binding-operator v0.9.0 h1:CS+eEtzu/PtWuyvYQFQpZXd6ukSuFtN+U0EKKtTsvlA=
|
github.com/redhat-developer/service-binding-operator v0.9.0 h1:CS+eEtzu/PtWuyvYQFQpZXd6ukSuFtN+U0EKKtTsvlA=
|
||||||
github.com/redhat-developer/service-binding-operator v0.9.0/go.mod h1:D415gZQiz5Q8zyRbmrNrlieb6Xp73oFtCb+nCuTL6GA=
|
github.com/redhat-developer/service-binding-operator v0.9.0/go.mod h1:D415gZQiz5Q8zyRbmrNrlieb6Xp73oFtCb+nCuTL6GA=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
|
github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
|
||||||
@@ -1299,6 +1303,8 @@ golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hM
|
|||||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
|
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
|
||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
|
||||||
|
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||||
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
|||||||
@@ -242,6 +242,7 @@ func createRegistryDevfiles(registry Registry, devfileIndex []indexSchema.Schema
|
|||||||
Registry: registry,
|
Registry: registry,
|
||||||
Language: devfileIndexEntry.Language,
|
Language: devfileIndexEntry.Language,
|
||||||
Tags: devfileIndexEntry.Tags,
|
Tags: devfileIndexEntry.Tags,
|
||||||
|
ProjectType: devfileIndexEntry.ProjectType,
|
||||||
}
|
}
|
||||||
registryDevfiles = append(registryDevfiles, stackDevfile)
|
registryDevfiles = append(registryDevfiles, stackDevfile)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ type DevfileComponentType struct {
|
|||||||
Registry Registry
|
Registry Registry
|
||||||
Language string
|
Language string
|
||||||
Tags []string
|
Tags []string
|
||||||
|
ProjectType string
|
||||||
}
|
}
|
||||||
|
|
||||||
// DevfileComponentTypeList lists all the DevfileComponentType's
|
// DevfileComponentTypeList lists all the DevfileComponentType's
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
package location
|
package location
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
|
||||||
"github.com/redhat-developer/odo/pkg/util"
|
"github.com/redhat-developer/odo/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -30,3 +32,47 @@ func DevfileLocation(contexDir string) string {
|
|||||||
devFile := DevfileFilenamesProvider(contexDir)
|
devFile := DevfileFilenamesProvider(contexDir)
|
||||||
return filepath.Join(contexDir, devFile)
|
return filepath.Join(contexDir, devFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsDevfileName returns true if name is a supported name for a devfile
|
||||||
|
func IsDevfileName(name string) bool {
|
||||||
|
for _, devFile := range possibleDevfileNames {
|
||||||
|
if devFile == name {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// DirectoryContainsDevfile returns true if the given directory contains a devfile with a supported name
|
||||||
|
func DirectoryContainsDevfile(fsys filesystem.Filesystem, dir string) (bool, error) {
|
||||||
|
for _, devFile := range possibleDevfileNames {
|
||||||
|
_, err := fsys.Stat(filepath.Join(dir, devFile))
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
continue
|
||||||
|
} else if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
// path to file does exist
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DirIsEmpty returns true if the given directory contains no file
|
||||||
|
func DirIsEmpty(fsys filesystem.Filesystem, path string) (bool, error) {
|
||||||
|
files, err := fsys.ReadDir(path)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return len(files) == 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DirContainsOnlyDevfile returns true if the directory contains only one file which is a devfile
|
||||||
|
// with a supported name
|
||||||
|
func DirContainsOnlyDevfile(fsys filesystem.Filesystem, path string) (bool, error) {
|
||||||
|
files, err := fsys.ReadDir(path)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return len(files) == 1 && IsDevfileName(files[0].Name()), nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -77,3 +77,16 @@ func (o *Survey) AskName(defaultName string) (string, error) {
|
|||||||
}
|
}
|
||||||
return answer, nil
|
return answer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *Survey) AskCorrect() (bool, error) {
|
||||||
|
question := &survey.Confirm{
|
||||||
|
Message: "Is this correct?",
|
||||||
|
Default: true,
|
||||||
|
}
|
||||||
|
var answer bool
|
||||||
|
err := survey.AskOne(question, &answer)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return answer, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,4 +19,7 @@ type Asker interface {
|
|||||||
|
|
||||||
// AskName asks for a devfile component name
|
// AskName asks for a devfile component name
|
||||||
AskName(defaultName string) (string, error)
|
AskName(defaultName string) (string, error)
|
||||||
|
|
||||||
|
// AskCorrect asks for confirmation
|
||||||
|
AskCorrect() (bool, error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,21 @@ func (m *MockAsker) EXPECT() *MockAskerMockRecorder {
|
|||||||
return m.recorder
|
return m.recorder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AskCorrect mocks base method.
|
||||||
|
func (m *MockAsker) AskCorrect() (bool, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "AskCorrect")
|
||||||
|
ret0, _ := ret[0].(bool)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// AskCorrect indicates an expected call of AskCorrect.
|
||||||
|
func (mr *MockAskerMockRecorder) AskCorrect() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AskCorrect", reflect.TypeOf((*MockAsker)(nil).AskCorrect))
|
||||||
|
}
|
||||||
|
|
||||||
// AskLanguage mocks base method.
|
// AskLanguage mocks base method.
|
||||||
func (m *MockAsker) AskLanguage(langs []string) (string, error) {
|
func (m *MockAsker) AskLanguage(langs []string) (string, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
|
|||||||
92
pkg/init/backend/alizer.go
Normal file
92
pkg/init/backend/alizer.go
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
package backend
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
|
||||||
|
"github.com/devfile/library/pkg/devfile/parser"
|
||||||
|
"github.com/redhat-developer/alizer/go/pkg/apis/recognizer"
|
||||||
|
"github.com/redhat-developer/odo/pkg/catalog"
|
||||||
|
"github.com/redhat-developer/odo/pkg/init/asker"
|
||||||
|
"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AlizerBackend struct {
|
||||||
|
askerClient asker.Asker
|
||||||
|
catalogClient catalog.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAlizerBackend(askerClient asker.Asker, catalogClient catalog.Client) *AlizerBackend {
|
||||||
|
return &AlizerBackend{
|
||||||
|
askerClient: askerClient,
|
||||||
|
catalogClient: catalogClient,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *AlizerBackend) Validate(flags map[string]string, fs filesystem.Filesystem, dir string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// detectFramework uses the alizer library in order to detect the devfile
|
||||||
|
// to use depending on the files in the path
|
||||||
|
func (o *AlizerBackend) detectFramework(path string) (recognizer.DevFileType, catalog.Registry, error) {
|
||||||
|
types := []recognizer.DevFileType{}
|
||||||
|
components, err := o.catalogClient.ListDevfileComponents("")
|
||||||
|
if err != nil {
|
||||||
|
return recognizer.DevFileType{}, catalog.Registry{}, err
|
||||||
|
}
|
||||||
|
for _, component := range components.Items {
|
||||||
|
types = append(types, recognizer.DevFileType{
|
||||||
|
Name: component.Name,
|
||||||
|
Language: component.Language,
|
||||||
|
ProjectType: component.ProjectType,
|
||||||
|
Tags: component.Tags,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
typ, err := recognizer.SelectDevFileFromTypes(path, types)
|
||||||
|
if err != nil {
|
||||||
|
return recognizer.DevFileType{}, catalog.Registry{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(feloy): This part won't be necessary when SelectDevFileFromTypes returns the index
|
||||||
|
var indexOfDetected int
|
||||||
|
for i, typeFromList := range types {
|
||||||
|
if reflect.DeepEqual(typeFromList, typ) {
|
||||||
|
indexOfDetected = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
registry := components.Items[indexOfDetected].Registry
|
||||||
|
return typ, registry, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectDevfile calls thz Alizer to detect the devfile and asks for confirmation to the user
|
||||||
|
func (o *AlizerBackend) SelectDevfile(flags map[string]string, fs filesystem.Filesystem, dir string) (location *DevfileLocation, err error) {
|
||||||
|
selected, registry, err := o.detectFramework(dir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Based on the files in the current directory odo detected\nLanguage: %s\nProject type: %s\n", selected.Language, selected.ProjectType)
|
||||||
|
fmt.Printf("The devfile %q from the registry %q will be downloaded.\n", selected.Name, registry.Name)
|
||||||
|
confirm, err := o.askerClient.AskCorrect()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !confirm {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return &DevfileLocation{
|
||||||
|
Devfile: selected.Name,
|
||||||
|
DevfileRegistry: registry.Name,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *AlizerBackend) SelectStarterProject(devfile parser.DevfileObj, flags map[string]string) (starter *v1alpha2.StarterProject, err error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *AlizerBackend) PersonalizeName(devfile parser.DevfileObj, flags map[string]string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
218
pkg/init/backend/alizer_test.go
Normal file
218
pkg/init/backend/alizer_test.go
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
package backend
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/redhat-developer/odo/pkg/catalog"
|
||||||
|
"github.com/redhat-developer/odo/pkg/init/asker"
|
||||||
|
"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Below functions are from:
|
||||||
|
// https://github.com/redhat-developer/alizer/blob/main/go/test/apis/language_recognizer_test.go
|
||||||
|
func GetTestProjectPath(folder string) string {
|
||||||
|
_, b, _, _ := runtime.Caller(0)
|
||||||
|
basepath := filepath.Dir(b)
|
||||||
|
return filepath.Join(basepath, "..", "..", "..", "tests/examples/source/", folder)
|
||||||
|
}
|
||||||
|
|
||||||
|
var types = []catalog.DevfileComponentType{
|
||||||
|
{
|
||||||
|
Name: "java-maven",
|
||||||
|
Language: "java",
|
||||||
|
ProjectType: "maven",
|
||||||
|
Tags: []string{"Java", "Maven"},
|
||||||
|
Registry: catalog.Registry{
|
||||||
|
Name: "registry1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "java-quarkus",
|
||||||
|
Language: "java",
|
||||||
|
ProjectType: "quarkus",
|
||||||
|
Tags: []string{"Java", "Quarkus"},
|
||||||
|
Registry: catalog.Registry{
|
||||||
|
Name: "registry1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "java-wildfly",
|
||||||
|
Language: "java",
|
||||||
|
ProjectType: "wildfly",
|
||||||
|
Tags: []string{"Java", "WildFly"},
|
||||||
|
Registry: catalog.Registry{
|
||||||
|
Name: "registry2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "nodejs",
|
||||||
|
Language: "javascript",
|
||||||
|
ProjectType: "nodejs",
|
||||||
|
Tags: []string{"NodeJS", "Express", "ubi8"},
|
||||||
|
Registry: catalog.Registry{
|
||||||
|
Name: "registry2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "python",
|
||||||
|
Language: "python",
|
||||||
|
ProjectType: "python",
|
||||||
|
Tags: []string{"Python", "pip"},
|
||||||
|
Registry: catalog.Registry{
|
||||||
|
Name: "registry3",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var list = catalog.DevfileComponentTypeList{
|
||||||
|
Items: types,
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDetectFramework(t *testing.T) {
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
wantedDevfile string
|
||||||
|
wantedRegistry string
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Detect Node.JS example",
|
||||||
|
args: args{
|
||||||
|
path: GetTestProjectPath("nodejs"),
|
||||||
|
},
|
||||||
|
wantedDevfile: "nodejs",
|
||||||
|
wantedRegistry: "registry2",
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Detect java openjdk example",
|
||||||
|
args: args{
|
||||||
|
path: GetTestProjectPath("openjdk"),
|
||||||
|
},
|
||||||
|
wantedDevfile: "java-maven",
|
||||||
|
wantedRegistry: "registry1",
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Detect python example",
|
||||||
|
args: args{
|
||||||
|
path: GetTestProjectPath("python"),
|
||||||
|
},
|
||||||
|
wantedDevfile: "python",
|
||||||
|
wantedRegistry: "registry3",
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
ctrl := gomock.NewController(t)
|
||||||
|
askerClient := asker.NewMockAsker(ctrl)
|
||||||
|
catalogClient := catalog.NewMockClient(ctrl)
|
||||||
|
catalogClient.EXPECT().ListDevfileComponents("").Return(list, nil)
|
||||||
|
alizerClient := NewAlizerBackend(askerClient, catalogClient)
|
||||||
|
// Run function DetectFramework
|
||||||
|
detected, registry, err := alizerClient.detectFramework(tt.args.path)
|
||||||
|
|
||||||
|
if !tt.wantErr == (err != nil) {
|
||||||
|
t.Errorf("unexpected error %v, wantErr %v", err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if detected.Name != tt.wantedDevfile {
|
||||||
|
t.Errorf("unexpected devfile %v, wantedDevfile %v", detected, tt.wantedDevfile)
|
||||||
|
}
|
||||||
|
if registry.Name != tt.wantedRegistry {
|
||||||
|
t.Errorf("unexpected registry %v, wantedRegistry %v", registry, tt.wantedRegistry)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAlizerBackend_SelectDevfile(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
askerClient func(ctrl *gomock.Controller) asker.Asker
|
||||||
|
catalogClient func(ctrl *gomock.Controller) catalog.Client
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
flags map[string]string
|
||||||
|
fs filesystem.Filesystem
|
||||||
|
dir string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
wantLocation *DevfileLocation
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "devfile found and accepted",
|
||||||
|
fields: fields{
|
||||||
|
askerClient: func(ctrl *gomock.Controller) asker.Asker {
|
||||||
|
askerClient := asker.NewMockAsker(ctrl)
|
||||||
|
askerClient.EXPECT().AskCorrect().Return(true, nil)
|
||||||
|
return askerClient
|
||||||
|
},
|
||||||
|
catalogClient: func(ctrl *gomock.Controller) catalog.Client {
|
||||||
|
catalogClient := catalog.NewMockClient(ctrl)
|
||||||
|
catalogClient.EXPECT().ListDevfileComponents("").Return(list, nil)
|
||||||
|
return catalogClient
|
||||||
|
},
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
fs: filesystem.DefaultFs{},
|
||||||
|
dir: GetTestProjectPath("nodejs"),
|
||||||
|
},
|
||||||
|
wantLocation: &DevfileLocation{
|
||||||
|
Devfile: "nodejs",
|
||||||
|
DevfileRegistry: "registry2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "devfile found but not accepted",
|
||||||
|
fields: fields{
|
||||||
|
askerClient: func(ctrl *gomock.Controller) asker.Asker {
|
||||||
|
askerClient := asker.NewMockAsker(ctrl)
|
||||||
|
askerClient.EXPECT().AskCorrect().Return(false, nil)
|
||||||
|
return askerClient
|
||||||
|
},
|
||||||
|
catalogClient: func(ctrl *gomock.Controller) catalog.Client {
|
||||||
|
catalogClient := catalog.NewMockClient(ctrl)
|
||||||
|
catalogClient.EXPECT().ListDevfileComponents("").Return(list, nil)
|
||||||
|
return catalogClient
|
||||||
|
},
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
fs: filesystem.DefaultFs{},
|
||||||
|
dir: GetTestProjectPath("nodejs"),
|
||||||
|
},
|
||||||
|
wantLocation: nil,
|
||||||
|
},
|
||||||
|
// TODO: Add test cases.
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
ctrl := gomock.NewController(t)
|
||||||
|
o := &AlizerBackend{
|
||||||
|
askerClient: tt.fields.askerClient(ctrl),
|
||||||
|
catalogClient: tt.fields.catalogClient(ctrl),
|
||||||
|
}
|
||||||
|
gotLocation, err := o.SelectDevfile(tt.args.flags, tt.args.fs, tt.args.dir)
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("AlizerBackend.SelectDevfile() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(gotLocation, tt.wantLocation) {
|
||||||
|
t.Errorf("AlizerBackend.SelectDevfile() = %v, want %v", gotLocation, tt.wantLocation)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,7 +9,9 @@ import (
|
|||||||
"github.com/devfile/library/pkg/devfile/parser/data/v2/common"
|
"github.com/devfile/library/pkg/devfile/parser/data/v2/common"
|
||||||
dfutil "github.com/devfile/library/pkg/util"
|
dfutil "github.com/devfile/library/pkg/util"
|
||||||
|
|
||||||
|
"github.com/redhat-developer/odo/pkg/devfile/location"
|
||||||
"github.com/redhat-developer/odo/pkg/preference"
|
"github.com/redhat-developer/odo/pkg/preference"
|
||||||
|
"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -31,7 +33,7 @@ func NewFlagsBackend(preferenceClient preference.Client) *FlagsBackend {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *FlagsBackend) Validate(flags map[string]string) error {
|
func (o *FlagsBackend) Validate(flags map[string]string, fs filesystem.Filesystem, dir string) error {
|
||||||
if flags[FLAG_NAME] == "" {
|
if flags[FLAG_NAME] == "" {
|
||||||
return errors.New("missing --name parameter: please add --name <name> to specify a name for the component")
|
return errors.New("missing --name parameter: please add --name <name> to specify a name for the component")
|
||||||
}
|
}
|
||||||
@@ -54,10 +56,19 @@ func (o *FlagsBackend) Validate(flags map[string]string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
empty, err := location.DirIsEmpty(fs, dir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !empty && flags[FLAG_STARTER] != "" {
|
||||||
|
return errors.New("--starter parameter cannot be used when the directory is not empty")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *FlagsBackend) SelectDevfile(flags map[string]string) (*DevfileLocation, error) {
|
func (o *FlagsBackend) SelectDevfile(flags map[string]string, _ filesystem.Filesystem, _ string) (*DevfileLocation, error) {
|
||||||
return &DevfileLocation{
|
return &DevfileLocation{
|
||||||
Devfile: flags[FLAG_DEVFILE],
|
Devfile: flags[FLAG_DEVFILE],
|
||||||
DevfileRegistry: flags[FLAG_DEVFILE_REGISTRY],
|
DevfileRegistry: flags[FLAG_DEVFILE_REGISTRY],
|
||||||
|
|||||||
@@ -10,9 +10,10 @@ import (
|
|||||||
"github.com/devfile/library/pkg/devfile/parser"
|
"github.com/devfile/library/pkg/devfile/parser"
|
||||||
parsercontext "github.com/devfile/library/pkg/devfile/parser/context"
|
parsercontext "github.com/devfile/library/pkg/devfile/parser/context"
|
||||||
"github.com/devfile/library/pkg/devfile/parser/data"
|
"github.com/devfile/library/pkg/devfile/parser/data"
|
||||||
"github.com/devfile/library/pkg/testingutil/filesystem"
|
dffilesystem "github.com/devfile/library/pkg/testingutil/filesystem"
|
||||||
|
|
||||||
"github.com/redhat-developer/odo/pkg/preference"
|
"github.com/redhat-developer/odo/pkg/preference"
|
||||||
|
"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFlagsBackend_SelectDevfile(t *testing.T) {
|
func TestFlagsBackend_SelectDevfile(t *testing.T) {
|
||||||
@@ -45,7 +46,7 @@ func TestFlagsBackend_SelectDevfile(t *testing.T) {
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
o := &FlagsBackend{}
|
o := &FlagsBackend{}
|
||||||
got, err := o.SelectDevfile(tt.fields.flags)
|
got, err := o.SelectDevfile(tt.fields.flags, nil, "")
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
t.Errorf("FlagsBackend.SelectDevfile() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("FlagsBackend.SelectDevfile() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
return
|
return
|
||||||
@@ -62,6 +63,8 @@ func TestFlagsBackend_Validate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
type args struct {
|
type args struct {
|
||||||
flags map[string]string
|
flags map[string]string
|
||||||
|
fsys func() filesystem.Filesystem
|
||||||
|
dir string
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@@ -77,6 +80,12 @@ func TestFlagsBackend_Validate(t *testing.T) {
|
|||||||
flags: map[string]string{
|
flags: map[string]string{
|
||||||
"name": "",
|
"name": "",
|
||||||
},
|
},
|
||||||
|
fsys: func() filesystem.Filesystem {
|
||||||
|
fs := filesystem.NewFakeFs()
|
||||||
|
_ = fs.MkdirAll("/tmp", 0644)
|
||||||
|
return fs
|
||||||
|
},
|
||||||
|
dir: "/tmp",
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
@@ -86,6 +95,12 @@ func TestFlagsBackend_Validate(t *testing.T) {
|
|||||||
flags: map[string]string{
|
flags: map[string]string{
|
||||||
"name": "aname",
|
"name": "aname",
|
||||||
},
|
},
|
||||||
|
fsys: func() filesystem.Filesystem {
|
||||||
|
fs := filesystem.NewFakeFs()
|
||||||
|
_ = fs.MkdirAll("/tmp", 0644)
|
||||||
|
return fs
|
||||||
|
},
|
||||||
|
dir: "/tmp",
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
@@ -96,6 +111,12 @@ func TestFlagsBackend_Validate(t *testing.T) {
|
|||||||
"name": "aname",
|
"name": "aname",
|
||||||
"devfile": "adevfile",
|
"devfile": "adevfile",
|
||||||
},
|
},
|
||||||
|
fsys: func() filesystem.Filesystem {
|
||||||
|
fs := filesystem.NewFakeFs()
|
||||||
|
_ = fs.MkdirAll("/tmp", 0644)
|
||||||
|
return fs
|
||||||
|
},
|
||||||
|
dir: "/tmp",
|
||||||
},
|
},
|
||||||
registryList: []preference.Registry{
|
registryList: []preference.Registry{
|
||||||
{
|
{
|
||||||
@@ -113,6 +134,12 @@ func TestFlagsBackend_Validate(t *testing.T) {
|
|||||||
"devfile": "adevfile",
|
"devfile": "adevfile",
|
||||||
"devfile-path": "apath",
|
"devfile-path": "apath",
|
||||||
},
|
},
|
||||||
|
fsys: func() filesystem.Filesystem {
|
||||||
|
fs := filesystem.NewFakeFs()
|
||||||
|
_ = fs.MkdirAll("/tmp", 0644)
|
||||||
|
return fs
|
||||||
|
},
|
||||||
|
dir: "/tmp",
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
@@ -124,6 +151,12 @@ func TestFlagsBackend_Validate(t *testing.T) {
|
|||||||
"devfile": "adevfile",
|
"devfile": "adevfile",
|
||||||
"devfile-registry": "aregistry",
|
"devfile-registry": "aregistry",
|
||||||
},
|
},
|
||||||
|
fsys: func() filesystem.Filesystem {
|
||||||
|
fs := filesystem.NewFakeFs()
|
||||||
|
_ = fs.MkdirAll("/tmp", 0644)
|
||||||
|
return fs
|
||||||
|
},
|
||||||
|
dir: "/tmp",
|
||||||
},
|
},
|
||||||
registryNameExists: true,
|
registryNameExists: true,
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
@@ -136,6 +169,12 @@ func TestFlagsBackend_Validate(t *testing.T) {
|
|||||||
"devfile": "adevfile",
|
"devfile": "adevfile",
|
||||||
"devfile-registry": "aregistry",
|
"devfile-registry": "aregistry",
|
||||||
},
|
},
|
||||||
|
fsys: func() filesystem.Filesystem {
|
||||||
|
fs := filesystem.NewFakeFs()
|
||||||
|
_ = fs.MkdirAll("/tmp", 0644)
|
||||||
|
return fs
|
||||||
|
},
|
||||||
|
dir: "/tmp",
|
||||||
},
|
},
|
||||||
registryNameExists: false,
|
registryNameExists: false,
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
@@ -148,6 +187,12 @@ func TestFlagsBackend_Validate(t *testing.T) {
|
|||||||
"devfile-path": "apath",
|
"devfile-path": "apath",
|
||||||
"devfile-registry": "aregistry",
|
"devfile-registry": "aregistry",
|
||||||
},
|
},
|
||||||
|
fsys: func() filesystem.Filesystem {
|
||||||
|
fs := filesystem.NewFakeFs()
|
||||||
|
_ = fs.MkdirAll("/tmp", 0644)
|
||||||
|
return fs
|
||||||
|
},
|
||||||
|
dir: "/tmp",
|
||||||
},
|
},
|
||||||
registryNameExists: true,
|
registryNameExists: true,
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
@@ -159,6 +204,12 @@ func TestFlagsBackend_Validate(t *testing.T) {
|
|||||||
"name": "1234",
|
"name": "1234",
|
||||||
"devfile": "adevfile",
|
"devfile": "adevfile",
|
||||||
},
|
},
|
||||||
|
fsys: func() filesystem.Filesystem {
|
||||||
|
fs := filesystem.NewFakeFs()
|
||||||
|
_ = fs.MkdirAll("/tmp", 0644)
|
||||||
|
return fs
|
||||||
|
},
|
||||||
|
dir: "/tmp",
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
@@ -169,6 +220,47 @@ func TestFlagsBackend_Validate(t *testing.T) {
|
|||||||
"name": "WrongName",
|
"name": "WrongName",
|
||||||
"devfile": "adevfile",
|
"devfile": "adevfile",
|
||||||
},
|
},
|
||||||
|
fsys: func() filesystem.Filesystem {
|
||||||
|
fs := filesystem.NewFakeFs()
|
||||||
|
_ = fs.MkdirAll("/tmp", 0644)
|
||||||
|
return fs
|
||||||
|
},
|
||||||
|
dir: "/tmp",
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "starter flag with an empty directory",
|
||||||
|
args: args{
|
||||||
|
flags: map[string]string{
|
||||||
|
"name": "aname",
|
||||||
|
"devfile": "adevfile",
|
||||||
|
"starter": "astarter",
|
||||||
|
},
|
||||||
|
fsys: func() filesystem.Filesystem {
|
||||||
|
fs := filesystem.NewFakeFs()
|
||||||
|
_ = fs.MkdirAll("/tmp", 0644)
|
||||||
|
return fs
|
||||||
|
},
|
||||||
|
dir: "/tmp",
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "starter flag with a non empty directory",
|
||||||
|
args: args{
|
||||||
|
flags: map[string]string{
|
||||||
|
"name": "aname",
|
||||||
|
"devfile": "adevfile",
|
||||||
|
"starter": "astarter",
|
||||||
|
},
|
||||||
|
fsys: func() filesystem.Filesystem {
|
||||||
|
fs := filesystem.NewFakeFs()
|
||||||
|
_ = fs.MkdirAll("/tmp", 0644)
|
||||||
|
_ = fs.WriteFile("/tmp/main.go", []byte("package main"), 0644)
|
||||||
|
return fs
|
||||||
|
},
|
||||||
|
dir: "/tmp",
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
@@ -184,7 +276,7 @@ func TestFlagsBackend_Validate(t *testing.T) {
|
|||||||
o := &FlagsBackend{
|
o := &FlagsBackend{
|
||||||
preferenceClient: prefClient,
|
preferenceClient: prefClient,
|
||||||
}
|
}
|
||||||
if err := o.Validate(tt.args.flags); (err != nil) != tt.wantErr {
|
if err := o.Validate(tt.args.flags, tt.args.fsys(), tt.args.dir); (err != nil) != tt.wantErr {
|
||||||
t.Errorf("FlagsBackend.Validate() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("FlagsBackend.Validate() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -258,9 +350,6 @@ func TestFlagsBackend_SelectStarterProject(t *testing.T) {
|
|||||||
{
|
{
|
||||||
Name: "starter1",
|
Name: "starter1",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
Name: "starter2",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Name: "starter3",
|
Name: "starter3",
|
||||||
},
|
},
|
||||||
@@ -301,7 +390,7 @@ func TestFlagsBackend_PersonalizeName(t *testing.T) {
|
|||||||
preferenceClient preference.Client
|
preferenceClient preference.Client
|
||||||
}
|
}
|
||||||
type args struct {
|
type args struct {
|
||||||
devfile func(fs filesystem.Filesystem) parser.DevfileObj
|
devfile func(fs dffilesystem.Filesystem) parser.DevfileObj
|
||||||
flags map[string]string
|
flags map[string]string
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
@@ -314,7 +403,7 @@ func TestFlagsBackend_PersonalizeName(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "name flag",
|
name: "name flag",
|
||||||
args: args{
|
args: args{
|
||||||
devfile: func(fs filesystem.Filesystem) parser.DevfileObj {
|
devfile: func(fs dffilesystem.Filesystem) parser.DevfileObj {
|
||||||
devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion200))
|
devfileData, _ := data.NewDevfileData(string(data.APISchemaVersion200))
|
||||||
obj := parser.DevfileObj{
|
obj := parser.DevfileObj{
|
||||||
Ctx: parsercontext.FakeContext(fs, "/tmp/devfile.yaml"),
|
Ctx: parsercontext.FakeContext(fs, "/tmp/devfile.yaml"),
|
||||||
@@ -338,7 +427,7 @@ func TestFlagsBackend_PersonalizeName(t *testing.T) {
|
|||||||
o := &FlagsBackend{
|
o := &FlagsBackend{
|
||||||
preferenceClient: tt.fields.preferenceClient,
|
preferenceClient: tt.fields.preferenceClient,
|
||||||
}
|
}
|
||||||
fs := filesystem.NewFakeFs()
|
fs := dffilesystem.NewFakeFs()
|
||||||
devfile := tt.args.devfile(fs)
|
devfile := tt.args.devfile(fs)
|
||||||
err := o.PersonalizeName(devfile, tt.args.flags)
|
err := o.PersonalizeName(devfile, tt.args.flags)
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"github.com/redhat-developer/odo/pkg/catalog"
|
"github.com/redhat-developer/odo/pkg/catalog"
|
||||||
"github.com/redhat-developer/odo/pkg/init/asker"
|
"github.com/redhat-developer/odo/pkg/init/asker"
|
||||||
|
"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -19,22 +20,22 @@ const (
|
|||||||
|
|
||||||
// InteractiveBackend is a backend that will ask information interactively using the `asker` package
|
// InteractiveBackend is a backend that will ask information interactively using the `asker` package
|
||||||
type InteractiveBackend struct {
|
type InteractiveBackend struct {
|
||||||
asker asker.Asker
|
askerClient asker.Asker
|
||||||
catalogClient catalog.Client
|
catalogClient catalog.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewInteractiveBackend(asker asker.Asker, catalogClient catalog.Client) *InteractiveBackend {
|
func NewInteractiveBackend(askerClient asker.Asker, catalogClient catalog.Client) *InteractiveBackend {
|
||||||
return &InteractiveBackend{
|
return &InteractiveBackend{
|
||||||
asker: asker,
|
askerClient: askerClient,
|
||||||
catalogClient: catalogClient,
|
catalogClient: catalogClient,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *InteractiveBackend) Validate(flags map[string]string) error {
|
func (o *InteractiveBackend) Validate(flags map[string]string, fs filesystem.Filesystem, dir string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *InteractiveBackend) SelectDevfile(flags map[string]string) (*DevfileLocation, error) {
|
func (o *InteractiveBackend) SelectDevfile(flags map[string]string, _ filesystem.Filesystem, _ string) (*DevfileLocation, error) {
|
||||||
result := &DevfileLocation{}
|
result := &DevfileLocation{}
|
||||||
devfileEntries, _ := o.catalogClient.ListDevfileComponents("")
|
devfileEntries, _ := o.catalogClient.ListDevfileComponents("")
|
||||||
|
|
||||||
@@ -48,7 +49,7 @@ loop:
|
|||||||
switch state {
|
switch state {
|
||||||
|
|
||||||
case STATE_ASK_LANG:
|
case STATE_ASK_LANG:
|
||||||
lang, err = o.asker.AskLanguage(langs)
|
lang, err = o.askerClient.AskLanguage(langs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -57,7 +58,7 @@ loop:
|
|||||||
case STATE_ASK_TYPE:
|
case STATE_ASK_TYPE:
|
||||||
types := devfileEntries.GetProjectTypes(lang)
|
types := devfileEntries.GetProjectTypes(lang)
|
||||||
var back bool
|
var back bool
|
||||||
back, details, err = o.asker.AskType(types)
|
back, details, err = o.askerClient.AskType(types)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -86,7 +87,7 @@ func (o *InteractiveBackend) SelectStarterProject(devfile parser.DevfileObj, fla
|
|||||||
names = append(names, starterProject.Name)
|
names = append(names, starterProject.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
ok, starter, err := o.asker.AskStarterProject(names)
|
ok, starter, err := o.askerClient.AskStarterProject(names)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -97,7 +98,7 @@ func (o *InteractiveBackend) SelectStarterProject(devfile parser.DevfileObj, fla
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (o *InteractiveBackend) PersonalizeName(devfile parser.DevfileObj, flags map[string]string) error {
|
func (o *InteractiveBackend) PersonalizeName(devfile parser.DevfileObj, flags map[string]string) error {
|
||||||
name, err := o.asker.AskName(fmt.Sprintf("my-%s-app", devfile.Data.GetMetadata().Name))
|
name, err := o.askerClient.AskName(fmt.Sprintf("my-%s-app", devfile.Data.GetMetadata().Name))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,10 +84,10 @@ func TestInteractiveBackend_SelectDevfile(t *testing.T) {
|
|||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
ctrl := gomock.NewController(t)
|
ctrl := gomock.NewController(t)
|
||||||
o := &InteractiveBackend{
|
o := &InteractiveBackend{
|
||||||
asker: tt.fields.buildAsker(ctrl),
|
askerClient: tt.fields.buildAsker(ctrl),
|
||||||
catalogClient: tt.fields.buildCatalogClient(ctrl),
|
catalogClient: tt.fields.buildCatalogClient(ctrl),
|
||||||
}
|
}
|
||||||
got, err := o.SelectDevfile(map[string]string{})
|
got, err := o.SelectDevfile(map[string]string{}, nil, "")
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
t.Errorf("InteractiveBuilder.ParamsBuild() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("InteractiveBuilder.ParamsBuild() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
return
|
return
|
||||||
@@ -180,7 +180,7 @@ func TestInteractiveBackend_SelectStarterProject(t *testing.T) {
|
|||||||
askerClient = tt.fields.asker(ctrl)
|
askerClient = tt.fields.asker(ctrl)
|
||||||
}
|
}
|
||||||
o := &InteractiveBackend{
|
o := &InteractiveBackend{
|
||||||
asker: askerClient,
|
askerClient: askerClient,
|
||||||
catalogClient: tt.fields.catalogClient,
|
catalogClient: tt.fields.catalogClient,
|
||||||
}
|
}
|
||||||
got1, err := o.SelectStarterProject(tt.args.devfile(), tt.args.flags)
|
got1, err := o.SelectStarterProject(tt.args.devfile(), tt.args.flags)
|
||||||
@@ -245,7 +245,7 @@ func TestInteractiveBackend_PersonalizeName(t *testing.T) {
|
|||||||
askerClient = tt.fields.asker(ctrl)
|
askerClient = tt.fields.asker(ctrl)
|
||||||
}
|
}
|
||||||
o := &InteractiveBackend{
|
o := &InteractiveBackend{
|
||||||
asker: askerClient,
|
askerClient: askerClient,
|
||||||
catalogClient: tt.fields.catalogClient,
|
catalogClient: tt.fields.catalogClient,
|
||||||
}
|
}
|
||||||
fs := filesystem.NewFakeFs()
|
fs := filesystem.NewFakeFs()
|
||||||
|
|||||||
@@ -6,15 +6,16 @@ package backend
|
|||||||
import (
|
import (
|
||||||
"github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
|
"github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
|
||||||
"github.com/devfile/library/pkg/devfile/parser"
|
"github.com/devfile/library/pkg/devfile/parser"
|
||||||
|
"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
|
||||||
)
|
)
|
||||||
|
|
||||||
// InitBackend is a specialized backend for steps of initiating a project, based on various input (either from CLI flags or interactively from user)
|
// InitBackend is a specialized backend for steps of initiating a project, based on various input (either from CLI flags or interactively from user)
|
||||||
type InitBackend interface {
|
type InitBackend interface {
|
||||||
// Validate returns an error if it does not validate the flags
|
// Validate returns an error if it does not validate the flags based on the directory content
|
||||||
Validate(flags map[string]string) error
|
Validate(flags map[string]string, fs filesystem.Filesystem, dir string) error
|
||||||
|
|
||||||
// SelectDevfile selects a devfile and returns its location information, depending on the flags
|
// SelectDevfile selects a devfile and returns its location information, depending on the flags
|
||||||
SelectDevfile(flags map[string]string) (location *DevfileLocation, err error)
|
SelectDevfile(flags map[string]string, fs filesystem.Filesystem, dir string) (location *DevfileLocation, err error)
|
||||||
|
|
||||||
// SelectStarterProject selects a starter project from the devfile and returns information about the starter project,
|
// SelectStarterProject selects a starter project from the devfile and returns information about the starter project,
|
||||||
// depending on the flags. If not starter project is selected, a nil starter is returned
|
// depending on the flags. If not starter project is selected, a nil starter is returned
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
v1alpha2 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
|
v1alpha2 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
|
||||||
parser "github.com/devfile/library/pkg/devfile/parser"
|
parser "github.com/devfile/library/pkg/devfile/parser"
|
||||||
gomock "github.com/golang/mock/gomock"
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
filesystem "github.com/redhat-developer/odo/pkg/testingutil/filesystem"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MockInitBackend is a mock of InitBackend interface.
|
// MockInitBackend is a mock of InitBackend interface.
|
||||||
@@ -50,18 +51,18 @@ func (mr *MockInitBackendMockRecorder) PersonalizeName(devfile, flags interface{
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SelectDevfile mocks base method.
|
// SelectDevfile mocks base method.
|
||||||
func (m *MockInitBackend) SelectDevfile(flags map[string]string) (*DevfileLocation, error) {
|
func (m *MockInitBackend) SelectDevfile(flags map[string]string, fs filesystem.Filesystem, dir string) (*DevfileLocation, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "SelectDevfile", flags)
|
ret := m.ctrl.Call(m, "SelectDevfile", flags, fs, dir)
|
||||||
ret0, _ := ret[0].(*DevfileLocation)
|
ret0, _ := ret[0].(*DevfileLocation)
|
||||||
ret1, _ := ret[1].(error)
|
ret1, _ := ret[1].(error)
|
||||||
return ret0, ret1
|
return ret0, ret1
|
||||||
}
|
}
|
||||||
|
|
||||||
// SelectDevfile indicates an expected call of SelectDevfile.
|
// SelectDevfile indicates an expected call of SelectDevfile.
|
||||||
func (mr *MockInitBackendMockRecorder) SelectDevfile(flags interface{}) *gomock.Call {
|
func (mr *MockInitBackendMockRecorder) SelectDevfile(flags, fs, dir interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectDevfile", reflect.TypeOf((*MockInitBackend)(nil).SelectDevfile), flags)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectDevfile", reflect.TypeOf((*MockInitBackend)(nil).SelectDevfile), flags, fs, dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SelectStarterProject mocks base method.
|
// SelectStarterProject mocks base method.
|
||||||
@@ -80,15 +81,15 @@ func (mr *MockInitBackendMockRecorder) SelectStarterProject(devfile, flags inter
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Validate mocks base method.
|
// Validate mocks base method.
|
||||||
func (m *MockInitBackend) Validate(flags map[string]string) error {
|
func (m *MockInitBackend) Validate(flags map[string]string, fs filesystem.Filesystem, dir string) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "Validate", flags)
|
ret := m.ctrl.Call(m, "Validate", flags, fs, dir)
|
||||||
ret0, _ := ret[0].(error)
|
ret0, _ := ret[0].(error)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate indicates an expected call of Validate.
|
// Validate indicates an expected call of Validate.
|
||||||
func (mr *MockInitBackendMockRecorder) Validate(flags interface{}) *gomock.Call {
|
func (mr *MockInitBackendMockRecorder) Validate(flags, fs, dir interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Validate", reflect.TypeOf((*MockInitBackend)(nil).Validate), flags)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Validate", reflect.TypeOf((*MockInitBackend)(nil).Validate), flags, fs, dir)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package init
|
package init
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -11,6 +12,7 @@ import (
|
|||||||
dfutil "github.com/devfile/library/pkg/util"
|
dfutil "github.com/devfile/library/pkg/util"
|
||||||
|
|
||||||
"github.com/redhat-developer/odo/pkg/catalog"
|
"github.com/redhat-developer/odo/pkg/catalog"
|
||||||
|
"github.com/redhat-developer/odo/pkg/devfile/location"
|
||||||
"github.com/redhat-developer/odo/pkg/init/asker"
|
"github.com/redhat-developer/odo/pkg/init/asker"
|
||||||
"github.com/redhat-developer/odo/pkg/init/backend"
|
"github.com/redhat-developer/odo/pkg/init/backend"
|
||||||
"github.com/redhat-developer/odo/pkg/init/registry"
|
"github.com/redhat-developer/odo/pkg/init/registry"
|
||||||
@@ -24,43 +26,71 @@ type InitClient struct {
|
|||||||
// Backends
|
// Backends
|
||||||
flagsBackend *backend.FlagsBackend
|
flagsBackend *backend.FlagsBackend
|
||||||
interactiveBackend *backend.InteractiveBackend
|
interactiveBackend *backend.InteractiveBackend
|
||||||
|
alizerBackend *backend.AlizerBackend
|
||||||
|
|
||||||
// Clients
|
// Clients
|
||||||
fsys filesystem.Filesystem
|
fsys filesystem.Filesystem
|
||||||
preferenceClient preference.Client
|
preferenceClient preference.Client
|
||||||
registryClient registry.Client
|
registryClient registry.Client
|
||||||
|
catalogClient catalog.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewInitClient(fsys filesystem.Filesystem, preferenceClient preference.Client, registryClient registry.Client) *InitClient {
|
func NewInitClient(fsys filesystem.Filesystem, preferenceClient preference.Client, registryClient registry.Client, catalogClient catalog.Client) *InitClient {
|
||||||
|
// We create the asker client and the backends here and not at the CLI level, as we want to hide these details to the CLI
|
||||||
|
askerClient := asker.NewSurveyAsker()
|
||||||
return &InitClient{
|
return &InitClient{
|
||||||
flagsBackend: backend.NewFlagsBackend(preferenceClient),
|
flagsBackend: backend.NewFlagsBackend(preferenceClient),
|
||||||
interactiveBackend: backend.NewInteractiveBackend(asker.NewSurveyAsker(), catalog.NewCatalogClient(fsys, preferenceClient)),
|
interactiveBackend: backend.NewInteractiveBackend(askerClient, catalogClient),
|
||||||
|
alizerBackend: backend.NewAlizerBackend(askerClient, catalogClient),
|
||||||
fsys: fsys,
|
fsys: fsys,
|
||||||
preferenceClient: preferenceClient,
|
preferenceClient: preferenceClient,
|
||||||
registryClient: registryClient,
|
registryClient: registryClient,
|
||||||
|
catalogClient: catalogClient,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate calls Validate method of the adequate backend
|
// Validate calls Validate method of the adequate backend
|
||||||
func (o *InitClient) Validate(flags map[string]string) error {
|
func (o *InitClient) Validate(flags map[string]string, fs filesystem.Filesystem, dir string) error {
|
||||||
var backend backend.InitBackend
|
var backend backend.InitBackend
|
||||||
if len(flags) == 0 {
|
if len(flags) == 0 {
|
||||||
backend = o.interactiveBackend
|
backend = o.interactiveBackend
|
||||||
} else {
|
} else {
|
||||||
backend = o.flagsBackend
|
backend = o.flagsBackend
|
||||||
}
|
}
|
||||||
return backend.Validate(flags)
|
return backend.Validate(flags, fs, dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SelectDevfile calls SelectDevfile methods of the adequate backend
|
// SelectDevfile calls SelectDevfile methods of the adequate backend
|
||||||
func (o *InitClient) SelectDevfile(flags map[string]string) (*backend.DevfileLocation, error) {
|
func (o *InitClient) SelectDevfile(flags map[string]string, fs filesystem.Filesystem, dir string) (*backend.DevfileLocation, error) {
|
||||||
var backend backend.InitBackend
|
var backend backend.InitBackend
|
||||||
if len(flags) == 0 {
|
|
||||||
|
empty, err := location.DirIsEmpty(fs, dir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if empty && len(flags) == 0 {
|
||||||
backend = o.interactiveBackend
|
backend = o.interactiveBackend
|
||||||
|
} else if len(flags) == 0 {
|
||||||
|
backend = o.alizerBackend
|
||||||
} else {
|
} else {
|
||||||
backend = o.flagsBackend
|
backend = o.flagsBackend
|
||||||
}
|
}
|
||||||
return backend.SelectDevfile(flags)
|
location, err := backend.SelectDevfile(flags, fs, dir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If Alizer failed to determine the devfile, run interactively
|
||||||
|
if location == nil {
|
||||||
|
if backend == o.alizerBackend {
|
||||||
|
backend = o.interactiveBackend
|
||||||
|
return backend.SelectDevfile(flags, fs, dir)
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("unable to determine the devfile location")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return location, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *InitClient) DownloadDevfile(devfileLocation *backend.DevfileLocation, destDir string) (string, error) {
|
func (o *InitClient) DownloadDevfile(devfileLocation *backend.DevfileLocation, destDir string) (string, error) {
|
||||||
@@ -152,10 +182,17 @@ func (o *InitClient) downloadFromRegistry(registryName string, devfile string, d
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SelectStarterProject calls SelectStarterProject methods of the adequate backend
|
// SelectStarterProject calls SelectStarterProject methods of the adequate backend
|
||||||
func (o *InitClient) SelectStarterProject(devfile parser.DevfileObj, flags map[string]string) (*v1alpha2.StarterProject, error) {
|
func (o *InitClient) SelectStarterProject(devfile parser.DevfileObj, flags map[string]string, fs filesystem.Filesystem, dir string) (*v1alpha2.StarterProject, error) {
|
||||||
var backend backend.InitBackend
|
var backend backend.InitBackend
|
||||||
if len(flags) == 0 {
|
|
||||||
|
onlyDevfile, err := location.DirContainsOnlyDevfile(fs, dir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if onlyDevfile && len(flags) == 0 {
|
||||||
backend = o.interactiveBackend
|
backend = o.interactiveBackend
|
||||||
|
} else if len(flags) == 0 {
|
||||||
|
backend = o.alizerBackend
|
||||||
} else {
|
} else {
|
||||||
backend = o.flagsBackend
|
backend = o.flagsBackend
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,15 +12,17 @@ import (
|
|||||||
"github.com/devfile/library/pkg/devfile/parser"
|
"github.com/devfile/library/pkg/devfile/parser"
|
||||||
|
|
||||||
"github.com/redhat-developer/odo/pkg/init/backend"
|
"github.com/redhat-developer/odo/pkg/init/backend"
|
||||||
|
"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client interface {
|
type Client interface {
|
||||||
// Validate checks for each backend if flags are valid
|
// Validate checks for each backend if flags are valid
|
||||||
Validate(flags map[string]string) error
|
Validate(flags map[string]string, fs filesystem.Filesystem, dir string) error
|
||||||
|
|
||||||
// SelectDevfile returns information about a devfile selected based on the flags, or
|
// SelectDevfile returns information about a devfile selected based on Alizer if the directory content,
|
||||||
|
// or based on the flags if the directory is empty, or
|
||||||
// interactively if flags is empty
|
// interactively if flags is empty
|
||||||
SelectDevfile(flags map[string]string) (*backend.DevfileLocation, error)
|
SelectDevfile(flags map[string]string, fs filesystem.Filesystem, dir string) (*backend.DevfileLocation, error)
|
||||||
|
|
||||||
// DownloadDevfile downloads a devfile given its location information and a destination directory
|
// DownloadDevfile downloads a devfile given its location information and a destination directory
|
||||||
// and returns the path of the downloaded file
|
// and returns the path of the downloaded file
|
||||||
@@ -28,7 +30,7 @@ type Client interface {
|
|||||||
|
|
||||||
// SelectStarterProject selects a starter project from the devfile and returns information about the starter project,
|
// SelectStarterProject selects a starter project from the devfile and returns information about the starter project,
|
||||||
// depending on the flags. If not starter project is selected, a nil starter is returned
|
// depending on the flags. If not starter project is selected, a nil starter is returned
|
||||||
SelectStarterProject(devfile parser.DevfileObj, flags map[string]string) (*v1alpha2.StarterProject, error)
|
SelectStarterProject(devfile parser.DevfileObj, flags map[string]string, fs filesystem.Filesystem, dir string) (*v1alpha2.StarterProject, error)
|
||||||
|
|
||||||
// DownloadStarterProject downloads the starter project referenced in devfile and stores it in dest directory
|
// DownloadStarterProject downloads the starter project referenced in devfile and stores it in dest directory
|
||||||
// WARNING: This will first remove all the content of dest.
|
// WARNING: This will first remove all the content of dest.
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
parser "github.com/devfile/library/pkg/devfile/parser"
|
parser "github.com/devfile/library/pkg/devfile/parser"
|
||||||
gomock "github.com/golang/mock/gomock"
|
gomock "github.com/golang/mock/gomock"
|
||||||
backend "github.com/redhat-developer/odo/pkg/init/backend"
|
backend "github.com/redhat-developer/odo/pkg/init/backend"
|
||||||
|
filesystem "github.com/redhat-developer/odo/pkg/testingutil/filesystem"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MockClient is a mock of Client interface.
|
// MockClient is a mock of Client interface.
|
||||||
@@ -80,45 +81,45 @@ func (mr *MockClientMockRecorder) PersonalizeName(devfile, flags interface{}) *g
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SelectDevfile mocks base method.
|
// SelectDevfile mocks base method.
|
||||||
func (m *MockClient) SelectDevfile(flags map[string]string) (*backend.DevfileLocation, error) {
|
func (m *MockClient) SelectDevfile(flags map[string]string, fs filesystem.Filesystem, dir string) (*backend.DevfileLocation, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "SelectDevfile", flags)
|
ret := m.ctrl.Call(m, "SelectDevfile", flags, fs, dir)
|
||||||
ret0, _ := ret[0].(*backend.DevfileLocation)
|
ret0, _ := ret[0].(*backend.DevfileLocation)
|
||||||
ret1, _ := ret[1].(error)
|
ret1, _ := ret[1].(error)
|
||||||
return ret0, ret1
|
return ret0, ret1
|
||||||
}
|
}
|
||||||
|
|
||||||
// SelectDevfile indicates an expected call of SelectDevfile.
|
// SelectDevfile indicates an expected call of SelectDevfile.
|
||||||
func (mr *MockClientMockRecorder) SelectDevfile(flags interface{}) *gomock.Call {
|
func (mr *MockClientMockRecorder) SelectDevfile(flags, fs, dir interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectDevfile", reflect.TypeOf((*MockClient)(nil).SelectDevfile), flags)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectDevfile", reflect.TypeOf((*MockClient)(nil).SelectDevfile), flags, fs, dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SelectStarterProject mocks base method.
|
// SelectStarterProject mocks base method.
|
||||||
func (m *MockClient) SelectStarterProject(devfile parser.DevfileObj, flags map[string]string) (*v1alpha2.StarterProject, error) {
|
func (m *MockClient) SelectStarterProject(devfile parser.DevfileObj, flags map[string]string, fs filesystem.Filesystem, dir string) (*v1alpha2.StarterProject, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "SelectStarterProject", devfile, flags)
|
ret := m.ctrl.Call(m, "SelectStarterProject", devfile, flags, fs, dir)
|
||||||
ret0, _ := ret[0].(*v1alpha2.StarterProject)
|
ret0, _ := ret[0].(*v1alpha2.StarterProject)
|
||||||
ret1, _ := ret[1].(error)
|
ret1, _ := ret[1].(error)
|
||||||
return ret0, ret1
|
return ret0, ret1
|
||||||
}
|
}
|
||||||
|
|
||||||
// SelectStarterProject indicates an expected call of SelectStarterProject.
|
// SelectStarterProject indicates an expected call of SelectStarterProject.
|
||||||
func (mr *MockClientMockRecorder) SelectStarterProject(devfile, flags interface{}) *gomock.Call {
|
func (mr *MockClientMockRecorder) SelectStarterProject(devfile, flags, fs, dir interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectStarterProject", reflect.TypeOf((*MockClient)(nil).SelectStarterProject), devfile, flags)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectStarterProject", reflect.TypeOf((*MockClient)(nil).SelectStarterProject), devfile, flags, fs, dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate mocks base method.
|
// Validate mocks base method.
|
||||||
func (m *MockClient) Validate(flags map[string]string) error {
|
func (m *MockClient) Validate(flags map[string]string, fs filesystem.Filesystem, dir string) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "Validate", flags)
|
ret := m.ctrl.Call(m, "Validate", flags, fs, dir)
|
||||||
ret0, _ := ret[0].(error)
|
ret0, _ := ret[0].(error)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate indicates an expected call of Validate.
|
// Validate indicates an expected call of Validate.
|
||||||
func (mr *MockClientMockRecorder) Validate(flags interface{}) *gomock.Call {
|
func (mr *MockClientMockRecorder) Validate(flags, fs, dir interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Validate", reflect.TypeOf((*MockClient)(nil).Validate), flags)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Validate", reflect.TypeOf((*MockClient)(nil).Validate), flags, fs, dir)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,11 +7,10 @@ package registry
|
|||||||
import (
|
import (
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
|
|
||||||
gomock "github.com/golang/mock/gomock"
|
|
||||||
|
|
||||||
v1alpha2 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
|
v1alpha2 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
|
||||||
dfutil "github.com/devfile/library/pkg/util"
|
util "github.com/devfile/library/pkg/util"
|
||||||
library "github.com/devfile/registry-support/registry-library/library"
|
library "github.com/devfile/registry-support/registry-library/library"
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MockClient is a mock of Client interface.
|
// MockClient is a mock of Client interface.
|
||||||
@@ -38,7 +37,7 @@ func (m *MockClient) EXPECT() *MockClientMockRecorder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DownloadFileInMemory mocks base method.
|
// DownloadFileInMemory mocks base method.
|
||||||
func (m *MockClient) DownloadFileInMemory(params dfutil.HTTPRequestParams) ([]byte, error) {
|
func (m *MockClient) DownloadFileInMemory(params util.HTTPRequestParams) ([]byte, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "DownloadFileInMemory", params)
|
ret := m.ctrl.Call(m, "DownloadFileInMemory", params)
|
||||||
ret0, _ := ret[0].([]byte)
|
ret0, _ := ret[0].([]byte)
|
||||||
|
|||||||
@@ -2,19 +2,25 @@ package deploy
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/devfile/library/pkg/devfile"
|
||||||
|
"github.com/devfile/library/pkg/devfile/parser"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/redhat-developer/odo/pkg/devfile/adapters"
|
"github.com/redhat-developer/odo/pkg/devfile/adapters"
|
||||||
"github.com/redhat-developer/odo/pkg/devfile/adapters/kubernetes"
|
"github.com/redhat-developer/odo/pkg/devfile/adapters/kubernetes"
|
||||||
|
"github.com/redhat-developer/odo/pkg/devfile/location"
|
||||||
"github.com/redhat-developer/odo/pkg/envinfo"
|
"github.com/redhat-developer/odo/pkg/envinfo"
|
||||||
"github.com/redhat-developer/odo/pkg/odo/cli/component"
|
"github.com/redhat-developer/odo/pkg/odo/cli/component"
|
||||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions/clientset"
|
"github.com/redhat-developer/odo/pkg/odo/genericclioptions/clientset"
|
||||||
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
|
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
|
||||||
|
"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/kubectl/pkg/util/templates"
|
"k8s.io/kubectl/pkg/util/templates"
|
||||||
|
"k8s.io/utils/pointer"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RecommendedCommandName is the recommended command name
|
// RecommendedCommandName is the recommended command name
|
||||||
@@ -25,6 +31,9 @@ type DeployOptions struct {
|
|||||||
// Context
|
// Context
|
||||||
*genericclioptions.Context
|
*genericclioptions.Context
|
||||||
|
|
||||||
|
// Clients
|
||||||
|
clientset *clientset.Clientset
|
||||||
|
|
||||||
// Flags
|
// Flags
|
||||||
contextFlag string
|
contextFlag string
|
||||||
}
|
}
|
||||||
@@ -40,10 +49,43 @@ func NewDeployOptions() *DeployOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (o *DeployOptions) SetClientset(clientset *clientset.Clientset) {
|
func (o *DeployOptions) SetClientset(clientset *clientset.Clientset) {
|
||||||
|
o.clientset = clientset
|
||||||
}
|
}
|
||||||
|
|
||||||
// Complete DeployOptions after they've been created
|
// Complete DeployOptions after they've been created
|
||||||
func (o *DeployOptions) Complete(cmdline cmdline.Cmdline, args []string) (err error) {
|
func (o *DeployOptions) Complete(cmdline cmdline.Cmdline, args []string) (err error) {
|
||||||
|
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
containsDevfile, err := location.DirectoryContainsDevfile(filesystem.DefaultFs{}, cwd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !containsDevfile {
|
||||||
|
devfileLocation, err2 := o.clientset.InitClient.SelectDevfile(map[string]string{}, o.clientset.FS, cwd)
|
||||||
|
if err2 != nil {
|
||||||
|
return err2
|
||||||
|
}
|
||||||
|
|
||||||
|
devfilePath, err2 := o.clientset.InitClient.DownloadDevfile(devfileLocation, cwd)
|
||||||
|
if err2 != nil {
|
||||||
|
return fmt.Errorf("unable to download devfile: %w", err2)
|
||||||
|
}
|
||||||
|
|
||||||
|
devfileObj, _, err2 := devfile.ParseDevfileAndValidate(parser.ParserArgs{Path: devfilePath, FlattenedDevfile: pointer.BoolPtr(false)})
|
||||||
|
if err2 != nil {
|
||||||
|
return fmt.Errorf("unable to download devfile: %w", err2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the name in the devfile and writes the devfile back to the disk
|
||||||
|
err = o.clientset.InitClient.PersonalizeName(devfileObj, map[string]string{})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to update the devfile's name: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
o.Context, err = genericclioptions.New(genericclioptions.NewCreateParameters(cmdline).NeedDevfile(o.contextFlag))
|
o.Context, err = genericclioptions.New(genericclioptions.NewCreateParameters(cmdline).NeedDevfile(o.contextFlag))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -105,10 +147,10 @@ func NewCmdDeploy(name, fullName string) *cobra.Command {
|
|||||||
genericclioptions.GenericRun(o, cmd, args)
|
genericclioptions.GenericRun(o, cmd, args)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
clientset.Add(deployCmd, clientset.INIT)
|
||||||
|
|
||||||
// Add a defined annotation in order to appear in the help menu
|
// Add a defined annotation in order to appear in the help menu
|
||||||
deployCmd.Annotations = map[string]string{"command": "utility"}
|
deployCmd.Annotations["command"] = "utility"
|
||||||
deployCmd.SetUsageTemplate(odoutil.CmdUsageTemplate)
|
deployCmd.SetUsageTemplate(odoutil.CmdUsageTemplate)
|
||||||
odoutil.AddContextFlag(deployCmd, &o.contextFlag)
|
|
||||||
return deployCmd
|
return deployCmd
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,15 +2,16 @@ package init
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/devfile/library/pkg/devfile"
|
"github.com/devfile/library/pkg/devfile"
|
||||||
"github.com/devfile/library/pkg/devfile/parser"
|
"github.com/devfile/library/pkg/devfile/parser"
|
||||||
|
|
||||||
"github.com/redhat-developer/odo/pkg/component"
|
"github.com/redhat-developer/odo/pkg/component"
|
||||||
|
"github.com/redhat-developer/odo/pkg/devfile/location"
|
||||||
"github.com/redhat-developer/odo/pkg/init/backend"
|
"github.com/redhat-developer/odo/pkg/init/backend"
|
||||||
"github.com/redhat-developer/odo/pkg/log"
|
"github.com/redhat-developer/odo/pkg/log"
|
||||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||||
@@ -18,7 +19,6 @@ import (
|
|||||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions/clientset"
|
"github.com/redhat-developer/odo/pkg/odo/genericclioptions/clientset"
|
||||||
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
|
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
|
||||||
scontext "github.com/redhat-developer/odo/pkg/segment/context"
|
scontext "github.com/redhat-developer/odo/pkg/segment/context"
|
||||||
"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
|
|
||||||
|
|
||||||
"k8s.io/kubectl/pkg/util/templates"
|
"k8s.io/kubectl/pkg/util/templates"
|
||||||
"k8s.io/utils/pointer"
|
"k8s.io/utils/pointer"
|
||||||
@@ -85,30 +85,23 @@ func (o *InitOptions) Complete(cmdline cmdline.Cmdline, args []string) (err erro
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
empty, err := isEmpty(o.clientset.FS, o.contextDir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !empty {
|
|
||||||
return errors.New("The current directory is not empty. You can bootstrap new component only in empty directory.\nIf you have existing code that you want to deploy use `odo deploy` or use `odo dev` command to quickly iterate on your component.")
|
|
||||||
}
|
|
||||||
|
|
||||||
o.flags = cmdline.GetFlags()
|
o.flags = cmdline.GetFlags()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func isEmpty(fsys filesystem.Filesystem, path string) (bool, error) {
|
|
||||||
files, err := fsys.ReadDir(path)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return len(files) == 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate validates the InitOptions based on completed values
|
// Validate validates the InitOptions based on completed values
|
||||||
func (o *InitOptions) Validate() error {
|
func (o *InitOptions) Validate() error {
|
||||||
err := o.clientset.InitClient.Validate(o.flags)
|
|
||||||
|
devfilePresent, err := location.DirectoryContainsDevfile(o.clientset.FS, o.contextDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if devfilePresent {
|
||||||
|
return errors.New("a devfile already exists in the current directory")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = o.clientset.InitClient.Validate(o.flags, o.clientset.FS, o.contextDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -125,21 +118,21 @@ func (o *InitOptions) Run() (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if starterDownloaded {
|
if starterDownloaded {
|
||||||
err = fmt.Errorf("%w\nThe command failed after downloading the starter project. By security, the directory is not cleaned up.", err)
|
err = fmt.Errorf("%w\nthe command failed after downloading the starter project. By security, the directory is not cleaned up", err)
|
||||||
} else {
|
} else {
|
||||||
_ = o.clientset.FS.Remove("devfile.yaml")
|
_ = o.clientset.FS.Remove("devfile.yaml")
|
||||||
err = fmt.Errorf("%w\nThe command failed, the devfile has been removed from current directory.", err)
|
err = fmt.Errorf("%w\nthe command failed, the devfile has been removed from current directory", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
o.devfileLocation, err = o.clientset.InitClient.SelectDevfile(o.flags)
|
o.devfileLocation, err = o.clientset.InitClient.SelectDevfile(o.flags, o.clientset.FS, o.contextDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
devfilePath, err := o.clientset.InitClient.DownloadDevfile(o.devfileLocation, o.contextDir)
|
devfilePath, err := o.clientset.InitClient.DownloadDevfile(o.devfileLocation, o.contextDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Unable to download devfile: %w", err)
|
return fmt.Errorf("unable to download devfile: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
devfileObj, _, err := devfile.ParseDevfileAndValidate(parser.ParserArgs{Path: devfilePath, FlattenedDevfile: pointer.BoolPtr(false)})
|
devfileObj, _, err := devfile.ParseDevfileAndValidate(parser.ParserArgs{Path: devfilePath, FlattenedDevfile: pointer.BoolPtr(false)})
|
||||||
@@ -149,7 +142,7 @@ func (o *InitOptions) Run() (err error) {
|
|||||||
|
|
||||||
scontext.SetComponentType(o.ctx, component.GetComponentTypeFromDevfileMetadata(devfileObj.Data.GetMetadata()))
|
scontext.SetComponentType(o.ctx, component.GetComponentTypeFromDevfileMetadata(devfileObj.Data.GetMetadata()))
|
||||||
|
|
||||||
starterInfo, err := o.clientset.InitClient.SelectStarterProject(devfileObj, o.flags)
|
starterInfo, err := o.clientset.InitClient.SelectStarterProject(devfileObj, o.flags, o.clientset.FS, o.contextDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,11 +25,12 @@ func TestInitOptions_Complete(t *testing.T) {
|
|||||||
name: "directory not empty",
|
name: "directory not empty",
|
||||||
cmdlineExpects: func(mock *cmdline.MockCmdline) {
|
cmdlineExpects: func(mock *cmdline.MockCmdline) {
|
||||||
mock.EXPECT().Context().Return(context.Background())
|
mock.EXPECT().Context().Return(context.Background())
|
||||||
|
mock.EXPECT().GetFlags().Times(1)
|
||||||
},
|
},
|
||||||
fsysPopulate: func(fsys filesystem.Filesystem) {
|
fsysPopulate: func(fsys filesystem.Filesystem) {
|
||||||
_ = fsys.WriteFile(".emptyfile", []byte(""), 0644)
|
_ = fsys.WriteFile(".emptyfile", []byte(""), 0644)
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "directory empty",
|
name: "directory empty",
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ const (
|
|||||||
// Clients will be created only once and be reused for sub-dependencies
|
// Clients will be created only once and be reused for sub-dependencies
|
||||||
var subdeps map[string][]string = map[string][]string{
|
var subdeps map[string][]string = map[string][]string{
|
||||||
CATALOG: {FILESYSTEM, PREFERENCE},
|
CATALOG: {FILESYSTEM, PREFERENCE},
|
||||||
INIT: {FILESYSTEM, PREFERENCE, REGISTRY},
|
INIT: {FILESYSTEM, PREFERENCE, REGISTRY, CATALOG},
|
||||||
PROJECT: {KUBERNETES_NULLABLE},
|
PROJECT: {KUBERNETES_NULLABLE},
|
||||||
/* Add sub-dependencies here, if any */
|
/* Add sub-dependencies here, if any */
|
||||||
}
|
}
|
||||||
@@ -112,7 +112,7 @@ func Fetch(command *cobra.Command) (*Clientset, error) {
|
|||||||
dep.CatalogClient = catalog.NewCatalogClient(dep.FS, dep.PreferenceClient)
|
dep.CatalogClient = catalog.NewCatalogClient(dep.FS, dep.PreferenceClient)
|
||||||
}
|
}
|
||||||
if isDefined(command, INIT) {
|
if isDefined(command, INIT) {
|
||||||
dep.InitClient = _init.NewInitClient(dep.FS, dep.PreferenceClient, dep.RegistryClient)
|
dep.InitClient = _init.NewInitClient(dep.FS, dep.PreferenceClient, dep.RegistryClient, dep.CatalogClient)
|
||||||
}
|
}
|
||||||
if isDefined(command, PROJECT) {
|
if isDefined(command, PROJECT) {
|
||||||
dep.ProjectClient = project.NewClient(dep.KubernetesClient)
|
dep.ProjectClient = project.NewClient(dep.KubernetesClient)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package genericclioptions
|
package genericclioptions
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/redhat-developer/odo/pkg/devfile"
|
"github.com/redhat-developer/odo/pkg/devfile"
|
||||||
@@ -133,18 +134,22 @@ func New(parameters CreateParameters) (*Context, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctx.devfilePath = location.DevfileLocation(parameters.componentContext)
|
ctx.devfilePath = location.DevfileLocation(parameters.componentContext)
|
||||||
isDevfile := odoutil.CheckPathExists(ctx.devfilePath)
|
if parameters.devfile {
|
||||||
if parameters.devfile && isDevfile {
|
isDevfile := odoutil.CheckPathExists(ctx.devfilePath)
|
||||||
// Parse devfile and validate
|
if isDevfile {
|
||||||
devObj, err := devfile.ParseAndValidateFromFile(ctx.devfilePath)
|
// Parse devfile and validate
|
||||||
if err != nil {
|
devObj, err := devfile.ParseAndValidateFromFile(ctx.devfilePath)
|
||||||
return nil, fmt.Errorf("failed to parse the devfile %s, with error: %s", ctx.devfilePath, err)
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse the devfile %s, with error: %s", ctx.devfilePath, err)
|
||||||
|
}
|
||||||
|
err = validate.ValidateDevfileData(devObj.Data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ctx.EnvSpecificInfo.SetDevfileObj(devObj)
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("no devfile found")
|
||||||
}
|
}
|
||||||
err = validate.ValidateDevfileData(devObj.Data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ctx.EnvSpecificInfo.SetDevfileObj(devObj)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Context{
|
return &Context{
|
||||||
|
|||||||
@@ -241,7 +241,7 @@ func TestNew(t *testing.T) {
|
|||||||
_ = fs.WriteFile(filepath.Join(prefixDir, "myapp", ".odo", "env", "env.yaml"), []byte{}, 0644)
|
_ = fs.WriteFile(filepath.Join(prefixDir, "myapp", ".odo", "env", "env.yaml"), []byte{}, 0644)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedErr: "",
|
expectedErr: "no devfile found",
|
||||||
expected: Context{
|
expected: Context{
|
||||||
internalCxt: internalCxt{
|
internalCxt: internalCxt{
|
||||||
project: "myproject",
|
project: "myproject",
|
||||||
|
|||||||
@@ -187,20 +187,6 @@ func (mr *MockClientMockRecorder) NewPreferenceList() *gomock.Call {
|
|||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewPreferenceList", reflect.TypeOf((*MockClient)(nil).NewPreferenceList))
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewPreferenceList", reflect.TypeOf((*MockClient)(nil).NewPreferenceList))
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegistryCacheTime mocks base method.
|
|
||||||
func (m *MockClient) RegistryCacheTime() *int {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "RegistryCacheTime")
|
|
||||||
ret0, _ := ret[0].(*int)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegistryCacheTime indicates an expected call of RegistryCacheTime.
|
|
||||||
func (mr *MockClientMockRecorder) RegistryCacheTime() *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegistryCacheTime", reflect.TypeOf((*MockClient)(nil).RegistryCacheTime))
|
|
||||||
}
|
|
||||||
|
|
||||||
// PushTimeout mocks base method.
|
// PushTimeout mocks base method.
|
||||||
func (m *MockClient) PushTimeout() *int {
|
func (m *MockClient) PushTimeout() *int {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
@@ -215,6 +201,20 @@ func (mr *MockClientMockRecorder) PushTimeout() *gomock.Call {
|
|||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PushTimeout", reflect.TypeOf((*MockClient)(nil).PushTimeout))
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PushTimeout", reflect.TypeOf((*MockClient)(nil).PushTimeout))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RegistryCacheTime mocks base method.
|
||||||
|
func (m *MockClient) RegistryCacheTime() *int {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "RegistryCacheTime")
|
||||||
|
ret0, _ := ret[0].(*int)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegistryCacheTime indicates an expected call of RegistryCacheTime.
|
||||||
|
func (mr *MockClientMockRecorder) RegistryCacheTime() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegistryCacheTime", reflect.TypeOf((*MockClient)(nil).RegistryCacheTime))
|
||||||
|
}
|
||||||
|
|
||||||
// RegistryHandler mocks base method.
|
// RegistryHandler mocks base method.
|
||||||
func (m *MockClient) RegistryHandler(operation, registryName, registryURL string, forceFlag, isSecure bool) error {
|
func (m *MockClient) RegistryHandler(operation, registryName, registryURL string, forceFlag, isSecure bool) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
|
|||||||
@@ -33,6 +33,16 @@ var _ = Describe("odo devfile init command tests", func() {
|
|||||||
files := helper.ListFilesInDir(commonVar.Context)
|
files := helper.ListFilesInDir(commonVar.Context)
|
||||||
Expect(len(files)).To(Equal(0))
|
Expect(len(files)).To(Equal(0))
|
||||||
})
|
})
|
||||||
|
By("running odo init in a directory containing a devfile.yaml", func() {
|
||||||
|
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-registry.yaml"), filepath.Join(commonVar.Context, "devfile.yaml"))
|
||||||
|
err := helper.Cmd("odo", "init").ShouldFail().Err()
|
||||||
|
Expect(err).To(ContainSubstring("a devfile already exists in the current directory"))
|
||||||
|
})
|
||||||
|
By("running odo init in a directory containing a .devfile.yaml", func() {
|
||||||
|
helper.CopyExampleDevFile(filepath.Join("source", "devfiles", "nodejs", "devfile-registry.yaml"), filepath.Join(commonVar.Context, ".devfile.yaml"))
|
||||||
|
err := helper.Cmd("odo", "init").ShouldFail().Err()
|
||||||
|
Expect(err).To(ContainSubstring("a devfile already exists in the current directory"))
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
When("running odo init with valid flags", func() {
|
When("running odo init with valid flags", func() {
|
||||||
@@ -46,6 +56,19 @@ var _ = Describe("odo devfile init command tests", func() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
When("running odo init from a directory with sources", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
helper.CopyExample(filepath.Join("source", "nodejs"), commonVar.Context)
|
||||||
|
})
|
||||||
|
It("should work without --starter flag", func() {
|
||||||
|
helper.Cmd("odo", "init", "--name", "aname", "--devfile", "nodejs").ShouldPass()
|
||||||
|
})
|
||||||
|
It("should not accept --starter flag", func() {
|
||||||
|
err := helper.Cmd("odo", "init", "--name", "aname", "--devfile", "nodejs", "--starter", "nodejs-starter").ShouldFail().Err()
|
||||||
|
Expect(err).To(ContainSubstring("--starter parameter cannot be used when the directory is not empty"))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
When("devfile contains parent URI", func() {
|
When("devfile contains parent URI", func() {
|
||||||
var originalKeyList []string
|
var originalKeyList []string
|
||||||
var srcDevfile string
|
var srcDevfile string
|
||||||
|
|||||||
277
vendor/github.com/redhat-developer/alizer/go/LICENSE
generated
vendored
Normal file
277
vendor/github.com/redhat-developer/alizer/go/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,277 @@
|
|||||||
|
Eclipse Public License - v 2.0
|
||||||
|
|
||||||
|
THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
|
||||||
|
PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION
|
||||||
|
OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
|
||||||
|
|
||||||
|
1. DEFINITIONS
|
||||||
|
|
||||||
|
"Contribution" means:
|
||||||
|
|
||||||
|
a) in the case of the initial Contributor, the initial content
|
||||||
|
Distributed under this Agreement, and
|
||||||
|
|
||||||
|
b) in the case of each subsequent Contributor:
|
||||||
|
i) changes to the Program, and
|
||||||
|
ii) additions to the Program;
|
||||||
|
where such changes and/or additions to the Program originate from
|
||||||
|
and are Distributed by that particular Contributor. A Contribution
|
||||||
|
"originates" from a Contributor if it was added to the Program by
|
||||||
|
such Contributor itself or anyone acting on such Contributor's behalf.
|
||||||
|
Contributions do not include changes or additions to the Program that
|
||||||
|
are not Modified Works.
|
||||||
|
|
||||||
|
"Contributor" means any person or entity that Distributes the Program.
|
||||||
|
|
||||||
|
"Licensed Patents" mean patent claims licensable by a Contributor which
|
||||||
|
are necessarily infringed by the use or sale of its Contribution alone
|
||||||
|
or when combined with the Program.
|
||||||
|
|
||||||
|
"Program" means the Contributions Distributed in accordance with this
|
||||||
|
Agreement.
|
||||||
|
|
||||||
|
"Recipient" means anyone who receives the Program under this Agreement
|
||||||
|
or any Secondary License (as applicable), including Contributors.
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source Code or other
|
||||||
|
form, that is based on (or derived from) the Program and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship.
|
||||||
|
|
||||||
|
"Modified Works" shall mean any work in Source Code or other form that
|
||||||
|
results from an addition to, deletion from, or modification of the
|
||||||
|
contents of the Program, including, for purposes of clarity any new file
|
||||||
|
in Source Code form that contains any contents of the Program. Modified
|
||||||
|
Works shall not include works that contain only declarations,
|
||||||
|
interfaces, types, classes, structures, or files of the Program solely
|
||||||
|
in each case in order to link to, bind by name, or subclass the Program
|
||||||
|
or Modified Works thereof.
|
||||||
|
|
||||||
|
"Distribute" means the acts of a) distributing or b) making available
|
||||||
|
in any manner that enables the transfer of a copy.
|
||||||
|
|
||||||
|
"Source Code" means the form of a Program preferred for making
|
||||||
|
modifications, including but not limited to software source code,
|
||||||
|
documentation source, and configuration files.
|
||||||
|
|
||||||
|
"Secondary License" means either the GNU General Public License,
|
||||||
|
Version 2.0, or any later versions of that license, including any
|
||||||
|
exceptions or additional permissions as identified by the initial
|
||||||
|
Contributor.
|
||||||
|
|
||||||
|
2. GRANT OF RIGHTS
|
||||||
|
|
||||||
|
a) Subject to the terms of this Agreement, each Contributor hereby
|
||||||
|
grants Recipient a non-exclusive, worldwide, royalty-free copyright
|
||||||
|
license to reproduce, prepare Derivative Works of, publicly display,
|
||||||
|
publicly perform, Distribute and sublicense the Contribution of such
|
||||||
|
Contributor, if any, and such Derivative Works.
|
||||||
|
|
||||||
|
b) Subject to the terms of this Agreement, each Contributor hereby
|
||||||
|
grants Recipient a non-exclusive, worldwide, royalty-free patent
|
||||||
|
license under Licensed Patents to make, use, sell, offer to sell,
|
||||||
|
import and otherwise transfer the Contribution of such Contributor,
|
||||||
|
if any, in Source Code or other form. This patent license shall
|
||||||
|
apply to the combination of the Contribution and the Program if, at
|
||||||
|
the time the Contribution is added by the Contributor, such addition
|
||||||
|
of the Contribution causes such combination to be covered by the
|
||||||
|
Licensed Patents. The patent license shall not apply to any other
|
||||||
|
combinations which include the Contribution. No hardware per se is
|
||||||
|
licensed hereunder.
|
||||||
|
|
||||||
|
c) Recipient understands that although each Contributor grants the
|
||||||
|
licenses to its Contributions set forth herein, no assurances are
|
||||||
|
provided by any Contributor that the Program does not infringe the
|
||||||
|
patent or other intellectual property rights of any other entity.
|
||||||
|
Each Contributor disclaims any liability to Recipient for claims
|
||||||
|
brought by any other entity based on infringement of intellectual
|
||||||
|
property rights or otherwise. As a condition to exercising the
|
||||||
|
rights and licenses granted hereunder, each Recipient hereby
|
||||||
|
assumes sole responsibility to secure any other intellectual
|
||||||
|
property rights needed, if any. For example, if a third party
|
||||||
|
patent license is required to allow Recipient to Distribute the
|
||||||
|
Program, it is Recipient's responsibility to acquire that license
|
||||||
|
before distributing the Program.
|
||||||
|
|
||||||
|
d) Each Contributor represents that to its knowledge it has
|
||||||
|
sufficient copyright rights in its Contribution, if any, to grant
|
||||||
|
the copyright license set forth in this Agreement.
|
||||||
|
|
||||||
|
e) Notwithstanding the terms of any Secondary License, no
|
||||||
|
Contributor makes additional grants to any Recipient (other than
|
||||||
|
those set forth in this Agreement) as a result of such Recipient's
|
||||||
|
receipt of the Program under the terms of a Secondary License
|
||||||
|
(if permitted under the terms of Section 3).
|
||||||
|
|
||||||
|
3. REQUIREMENTS
|
||||||
|
|
||||||
|
3.1 If a Contributor Distributes the Program in any form, then:
|
||||||
|
|
||||||
|
a) the Program must also be made available as Source Code, in
|
||||||
|
accordance with section 3.2, and the Contributor must accompany
|
||||||
|
the Program with a statement that the Source Code for the Program
|
||||||
|
is available under this Agreement, and informs Recipients how to
|
||||||
|
obtain it in a reasonable manner on or through a medium customarily
|
||||||
|
used for software exchange; and
|
||||||
|
|
||||||
|
b) the Contributor may Distribute the Program under a license
|
||||||
|
different than this Agreement, provided that such license:
|
||||||
|
i) effectively disclaims on behalf of all other Contributors all
|
||||||
|
warranties and conditions, express and implied, including
|
||||||
|
warranties or conditions of title and non-infringement, and
|
||||||
|
implied warranties or conditions of merchantability and fitness
|
||||||
|
for a particular purpose;
|
||||||
|
|
||||||
|
ii) effectively excludes on behalf of all other Contributors all
|
||||||
|
liability for damages, including direct, indirect, special,
|
||||||
|
incidental and consequential damages, such as lost profits;
|
||||||
|
|
||||||
|
iii) does not attempt to limit or alter the recipients' rights
|
||||||
|
in the Source Code under section 3.2; and
|
||||||
|
|
||||||
|
iv) requires any subsequent distribution of the Program by any
|
||||||
|
party to be under a license that satisfies the requirements
|
||||||
|
of this section 3.
|
||||||
|
|
||||||
|
3.2 When the Program is Distributed as Source Code:
|
||||||
|
|
||||||
|
a) it must be made available under this Agreement, or if the
|
||||||
|
Program (i) is combined with other material in a separate file or
|
||||||
|
files made available under a Secondary License, and (ii) the initial
|
||||||
|
Contributor attached to the Source Code the notice described in
|
||||||
|
Exhibit A of this Agreement, then the Program may be made available
|
||||||
|
under the terms of such Secondary Licenses, and
|
||||||
|
|
||||||
|
b) a copy of this Agreement must be included with each copy of
|
||||||
|
the Program.
|
||||||
|
|
||||||
|
3.3 Contributors may not remove or alter any copyright, patent,
|
||||||
|
trademark, attribution notices, disclaimers of warranty, or limitations
|
||||||
|
of liability ("notices") contained within the Program from any copy of
|
||||||
|
the Program which they Distribute, provided that Contributors may add
|
||||||
|
their own appropriate notices.
|
||||||
|
|
||||||
|
4. COMMERCIAL DISTRIBUTION
|
||||||
|
|
||||||
|
Commercial distributors of software may accept certain responsibilities
|
||||||
|
with respect to end users, business partners and the like. While this
|
||||||
|
license is intended to facilitate the commercial use of the Program,
|
||||||
|
the Contributor who includes the Program in a commercial product
|
||||||
|
offering should do so in a manner which does not create potential
|
||||||
|
liability for other Contributors. Therefore, if a Contributor includes
|
||||||
|
the Program in a commercial product offering, such Contributor
|
||||||
|
("Commercial Contributor") hereby agrees to defend and indemnify every
|
||||||
|
other Contributor ("Indemnified Contributor") against any losses,
|
||||||
|
damages and costs (collectively "Losses") arising from claims, lawsuits
|
||||||
|
and other legal actions brought by a third party against the Indemnified
|
||||||
|
Contributor to the extent caused by the acts or omissions of such
|
||||||
|
Commercial Contributor in connection with its distribution of the Program
|
||||||
|
in a commercial product offering. The obligations in this section do not
|
||||||
|
apply to any claims or Losses relating to any actual or alleged
|
||||||
|
intellectual property infringement. In order to qualify, an Indemnified
|
||||||
|
Contributor must: a) promptly notify the Commercial Contributor in
|
||||||
|
writing of such claim, and b) allow the Commercial Contributor to control,
|
||||||
|
and cooperate with the Commercial Contributor in, the defense and any
|
||||||
|
related settlement negotiations. The Indemnified Contributor may
|
||||||
|
participate in any such claim at its own expense.
|
||||||
|
|
||||||
|
For example, a Contributor might include the Program in a commercial
|
||||||
|
product offering, Product X. That Contributor is then a Commercial
|
||||||
|
Contributor. If that Commercial Contributor then makes performance
|
||||||
|
claims, or offers warranties related to Product X, those performance
|
||||||
|
claims and warranties are such Commercial Contributor's responsibility
|
||||||
|
alone. Under this section, the Commercial Contributor would have to
|
||||||
|
defend claims against the other Contributors related to those performance
|
||||||
|
claims and warranties, and if a court requires any other Contributor to
|
||||||
|
pay any damages as a result, the Commercial Contributor must pay
|
||||||
|
those damages.
|
||||||
|
|
||||||
|
5. NO WARRANTY
|
||||||
|
|
||||||
|
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
|
||||||
|
PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS"
|
||||||
|
BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
|
||||||
|
IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF
|
||||||
|
TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. Each Recipient is solely responsible for determining the
|
||||||
|
appropriateness of using and distributing the Program and assumes all
|
||||||
|
risks associated with its exercise of rights under this Agreement,
|
||||||
|
including but not limited to the risks and costs of program errors,
|
||||||
|
compliance with applicable laws, damage to or loss of data, programs
|
||||||
|
or equipment, and unavailability or interruption of operations.
|
||||||
|
|
||||||
|
6. DISCLAIMER OF LIABILITY
|
||||||
|
|
||||||
|
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
|
||||||
|
PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS
|
||||||
|
SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST
|
||||||
|
PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
|
||||||
|
EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
|
||||||
|
7. GENERAL
|
||||||
|
|
||||||
|
If any provision of this Agreement is invalid or unenforceable under
|
||||||
|
applicable law, it shall not affect the validity or enforceability of
|
||||||
|
the remainder of the terms of this Agreement, and without further
|
||||||
|
action by the parties hereto, such provision shall be reformed to the
|
||||||
|
minimum extent necessary to make such provision valid and enforceable.
|
||||||
|
|
||||||
|
If Recipient institutes patent litigation against any entity
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that the
|
||||||
|
Program itself (excluding combinations of the Program with other software
|
||||||
|
or hardware) infringes such Recipient's patent(s), then such Recipient's
|
||||||
|
rights granted under Section 2(b) shall terminate as of the date such
|
||||||
|
litigation is filed.
|
||||||
|
|
||||||
|
All Recipient's rights under this Agreement shall terminate if it
|
||||||
|
fails to comply with any of the material terms or conditions of this
|
||||||
|
Agreement and does not cure such failure in a reasonable period of
|
||||||
|
time after becoming aware of such noncompliance. If all Recipient's
|
||||||
|
rights under this Agreement terminate, Recipient agrees to cease use
|
||||||
|
and distribution of the Program as soon as reasonably practicable.
|
||||||
|
However, Recipient's obligations under this Agreement and any licenses
|
||||||
|
granted by Recipient relating to the Program shall continue and survive.
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute copies of this Agreement,
|
||||||
|
but in order to avoid inconsistency the Agreement is copyrighted and
|
||||||
|
may only be modified in the following manner. The Agreement Steward
|
||||||
|
reserves the right to publish new versions (including revisions) of
|
||||||
|
this Agreement from time to time. No one other than the Agreement
|
||||||
|
Steward has the right to modify this Agreement. The Eclipse Foundation
|
||||||
|
is the initial Agreement Steward. The Eclipse Foundation may assign the
|
||||||
|
responsibility to serve as the Agreement Steward to a suitable separate
|
||||||
|
entity. Each new version of the Agreement will be given a distinguishing
|
||||||
|
version number. The Program (including Contributions) may always be
|
||||||
|
Distributed subject to the version of the Agreement under which it was
|
||||||
|
received. In addition, after a new version of the Agreement is published,
|
||||||
|
Contributor may elect to Distribute the Program (including its
|
||||||
|
Contributions) under the new version.
|
||||||
|
|
||||||
|
Except as expressly stated in Sections 2(a) and 2(b) above, Recipient
|
||||||
|
receives no rights or licenses to the intellectual property of any
|
||||||
|
Contributor under this Agreement, whether expressly, by implication,
|
||||||
|
estoppel or otherwise. All rights in the Program not expressly granted
|
||||||
|
under this Agreement are reserved. Nothing in this Agreement is intended
|
||||||
|
to be enforceable by any entity that is not a Contributor or Recipient.
|
||||||
|
No third-party beneficiary rights are created under this Agreement.
|
||||||
|
|
||||||
|
Exhibit A - Form of Secondary Licenses Notice
|
||||||
|
|
||||||
|
"This Source Code may also be made available under the following
|
||||||
|
Secondary Licenses when the conditions for such availability set forth
|
||||||
|
in the Eclipse Public License, v. 2.0 are satisfied: {name license(s),
|
||||||
|
version(s), and exceptions or additional permissions here}."
|
||||||
|
|
||||||
|
Simply including a copy of this Agreement, including this Exhibit A
|
||||||
|
is not sufficient to license the Source Code under Secondary Licenses.
|
||||||
|
|
||||||
|
If it is not possible or desirable to put the notice in a particular
|
||||||
|
file, then You may include the notice in a location (such as a LICENSE
|
||||||
|
file in a relevant directory) where a recipient would be likely to
|
||||||
|
look for such a notice.
|
||||||
|
|
||||||
|
You may add additional accurate notices of copyright ownership.
|
||||||
46
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/dotnet_enricher.go
generated
vendored
Normal file
46
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/dotnet_enricher.go
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2022 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 recognizer
|
||||||
|
|
||||||
|
import (
|
||||||
|
framework "github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/dotnet"
|
||||||
|
"github.com/redhat-developer/alizer/go/pkg/apis/language"
|
||||||
|
utils "github.com/redhat-developer/alizer/go/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DotNetEnricher struct{}
|
||||||
|
|
||||||
|
func (j DotNetEnricher) GetSupportedLanguages() []string {
|
||||||
|
return []string{"c#", "f#", "visual basic .net"}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDotNetFrameworkDetectors() []FrameworkDetectorWithConfigFile {
|
||||||
|
return []FrameworkDetectorWithConfigFile{
|
||||||
|
&framework.DotNetDetector{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j DotNetEnricher) DoEnrichLanguage(language *language.Language, files *[]string) {
|
||||||
|
configFiles := utils.GetFilesByRegex(files, ".*\\.\\w+proj")
|
||||||
|
for _, configFile := range configFiles {
|
||||||
|
getDotNetFrameworks(language, configFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j DotNetEnricher) IsConfigValidForComponentDetection(language string, config string) bool {
|
||||||
|
return IsConfigurationValidForLanguage(language, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDotNetFrameworks(language *language.Language, configFile string) {
|
||||||
|
for _, detector := range getDotNetFrameworkDetectors() {
|
||||||
|
detector.DoFrameworkDetection(language, configFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
109
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/enricher.go
generated
vendored
Normal file
109
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/enricher.go
generated
vendored
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2021 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 recognizer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/redhat-developer/alizer/go/pkg/apis/language"
|
||||||
|
"github.com/redhat-developer/alizer/go/pkg/utils/langfiles"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Enricher interface {
|
||||||
|
GetSupportedLanguages() []string
|
||||||
|
DoEnrichLanguage(language *language.Language, files *[]string)
|
||||||
|
IsConfigValidForComponentDetection(language string, configFile string) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type FrameworkDetectorWithConfigFile interface {
|
||||||
|
DoFrameworkDetection(language *language.Language, config string)
|
||||||
|
}
|
||||||
|
|
||||||
|
type FrameworkDetectorWithoutConfigFile interface {
|
||||||
|
DoFrameworkDetection(language *language.Language, files *[]string)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
IsConfigurationValidForLanguage check whether the configuration file is valid for current language.
|
||||||
|
For example when analyzing a nodejs project, we could find a package.json
|
||||||
|
within the node_modules folder. That is not to be considered valid
|
||||||
|
for component detection.
|
||||||
|
Paramenters:
|
||||||
|
language: language name
|
||||||
|
file: configuration file name
|
||||||
|
Returns:
|
||||||
|
bool: true if config file is valid for current language
|
||||||
|
|
||||||
|
*/
|
||||||
|
func IsConfigurationValidForLanguage(language string, file string) bool {
|
||||||
|
languageItem, err := langfiles.Get().GetLanguageByName(language)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, excludeFolder := range languageItem.ExcludeFolders {
|
||||||
|
if isFolderNameIncludedInPath(file, excludeFolder) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
isFolderNameIncludedInPath check if fullpath contains potentialSubFolderName
|
||||||
|
Parameters:
|
||||||
|
fullPath: complete path of a file
|
||||||
|
potentialSubFolderName: folder name
|
||||||
|
Returns:
|
||||||
|
bool: true if potentialSubFolderName is included in fullPath
|
||||||
|
*/
|
||||||
|
func isFolderNameIncludedInPath(fullPath string, potentialSubFolderName string) bool {
|
||||||
|
pathSeparator := fmt.Sprintf("%c", os.PathSeparator)
|
||||||
|
dir, _ := filepath.Split(fullPath)
|
||||||
|
|
||||||
|
subDirectories := strings.Split(dir, pathSeparator)
|
||||||
|
for _, subDir := range subDirectories {
|
||||||
|
if strings.EqualFold(subDir, potentialSubFolderName) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func getEnrichers() []Enricher {
|
||||||
|
return []Enricher{
|
||||||
|
&JavaEnricher{},
|
||||||
|
&JavaScriptEnricher{},
|
||||||
|
&PythonEnricher{},
|
||||||
|
&DotNetEnricher{},
|
||||||
|
&GoEnricher{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetEnricherByLanguage(language string) Enricher {
|
||||||
|
for _, enricher := range getEnrichers() {
|
||||||
|
if isLanguageSupportedByEnricher(language, enricher) {
|
||||||
|
return enricher
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isLanguageSupportedByEnricher(nameLanguage string, enricher Enricher) bool {
|
||||||
|
for _, language := range enricher.GetSupportedLanguages() {
|
||||||
|
if strings.EqualFold(language, nameLanguage) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
64
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/dotnet/dotnet_detector.go
generated
vendored
Normal file
64
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/dotnet/dotnet_detector.go
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2022 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 recognizer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/redhat-developer/alizer/go/pkg/apis/language"
|
||||||
|
"github.com/redhat-developer/alizer/go/pkg/schema"
|
||||||
|
"github.com/redhat-developer/alizer/go/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DotNetDetector struct{}
|
||||||
|
|
||||||
|
func (m DotNetDetector) DoFrameworkDetection(language *language.Language, configFilePath string) {
|
||||||
|
framework := getFrameworks(configFilePath)
|
||||||
|
if framework == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var frameworks []string
|
||||||
|
if strings.Contains(framework, ";") {
|
||||||
|
frameworks = strings.Split(framework, ";")
|
||||||
|
} else {
|
||||||
|
frameworks = []string{framework}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, frm := range frameworks {
|
||||||
|
if !utils.Contains(language.Frameworks, frm) {
|
||||||
|
language.Frameworks = append(language.Frameworks, frm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFrameworks(configFilePath string) string {
|
||||||
|
xmlFile, err := os.Open(configFilePath)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
byteValue, _ := ioutil.ReadAll(xmlFile)
|
||||||
|
|
||||||
|
var proj schema.DotNetProject
|
||||||
|
xml.Unmarshal(byteValue, &proj)
|
||||||
|
|
||||||
|
defer xmlFile.Close()
|
||||||
|
if proj.PropertyGroup.TargetFramework != "" {
|
||||||
|
return proj.PropertyGroup.TargetFramework
|
||||||
|
} else if proj.PropertyGroup.TargetFrameworkVersion != "" {
|
||||||
|
return proj.PropertyGroup.TargetFrameworkVersion
|
||||||
|
} else if proj.PropertyGroup.TargetFrameworks != "" {
|
||||||
|
return proj.PropertyGroup.TargetFrameworks
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
24
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go/beego_detector.go
generated
vendored
Normal file
24
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go/beego_detector.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2022 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 recognizer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/redhat-developer/alizer/go/pkg/apis/language"
|
||||||
|
"golang.org/x/mod/modfile"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BeegoDetector struct{}
|
||||||
|
|
||||||
|
func (e BeegoDetector) DoFrameworkDetection(language *language.Language, goMod *modfile.File) {
|
||||||
|
if hasFramework(goMod.Require, "github.com/beego/beego") {
|
||||||
|
language.Frameworks = append(language.Frameworks, "Beego")
|
||||||
|
}
|
||||||
|
}
|
||||||
24
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go/echo_detector.go
generated
vendored
Normal file
24
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go/echo_detector.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2022 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 recognizer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/redhat-developer/alizer/go/pkg/apis/language"
|
||||||
|
"golang.org/x/mod/modfile"
|
||||||
|
)
|
||||||
|
|
||||||
|
type EchoDetector struct{}
|
||||||
|
|
||||||
|
func (e EchoDetector) DoFrameworkDetection(language *language.Language, goMod *modfile.File) {
|
||||||
|
if hasFramework(goMod.Require, "github.com/labstack/echo") {
|
||||||
|
language.Frameworks = append(language.Frameworks, "Echo")
|
||||||
|
}
|
||||||
|
}
|
||||||
24
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go/fasthttp_detector.go
generated
vendored
Normal file
24
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go/fasthttp_detector.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2022 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 recognizer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/redhat-developer/alizer/go/pkg/apis/language"
|
||||||
|
"golang.org/x/mod/modfile"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FastHttpDetector struct{}
|
||||||
|
|
||||||
|
func (e FastHttpDetector) DoFrameworkDetection(language *language.Language, goMod *modfile.File) {
|
||||||
|
if hasFramework(goMod.Require, "github.com/valyala/fasthttp") {
|
||||||
|
language.Frameworks = append(language.Frameworks, "FastHttp")
|
||||||
|
}
|
||||||
|
}
|
||||||
24
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go/gin_detector.go
generated
vendored
Normal file
24
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go/gin_detector.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2022 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 recognizer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/redhat-developer/alizer/go/pkg/apis/language"
|
||||||
|
"golang.org/x/mod/modfile"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GinDetector struct{}
|
||||||
|
|
||||||
|
func (e GinDetector) DoFrameworkDetection(language *language.Language, goMod *modfile.File) {
|
||||||
|
if hasFramework(goMod.Require, "github.com/gin-gonic/gin") {
|
||||||
|
language.Frameworks = append(language.Frameworks, "Gin")
|
||||||
|
}
|
||||||
|
}
|
||||||
26
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go/go_detector.go
generated
vendored
Normal file
26
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go/go_detector.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2021 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 recognizer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/mod/modfile"
|
||||||
|
)
|
||||||
|
|
||||||
|
func hasFramework(modules []*modfile.Require, tag string) bool {
|
||||||
|
for _, module := range modules {
|
||||||
|
if strings.EqualFold(module.Mod.Path, tag) || strings.HasPrefix(module.Mod.Path, tag) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
24
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go/gofiber_detector.go
generated
vendored
Normal file
24
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go/gofiber_detector.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2022 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 recognizer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/redhat-developer/alizer/go/pkg/apis/language"
|
||||||
|
"golang.org/x/mod/modfile"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GoFiberDetector struct{}
|
||||||
|
|
||||||
|
func (e GoFiberDetector) DoFrameworkDetection(language *language.Language, goMod *modfile.File) {
|
||||||
|
if hasFramework(goMod.Require, "github.com/gofiber/fiber") {
|
||||||
|
language.Frameworks = append(language.Frameworks, "GoFiber")
|
||||||
|
}
|
||||||
|
}
|
||||||
24
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go/mux_detector.go
generated
vendored
Normal file
24
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go/mux_detector.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2022 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 recognizer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/redhat-developer/alizer/go/pkg/apis/language"
|
||||||
|
"golang.org/x/mod/modfile"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MuxDetector struct{}
|
||||||
|
|
||||||
|
func (e MuxDetector) DoFrameworkDetection(language *language.Language, goMod *modfile.File) {
|
||||||
|
if hasFramework(goMod.Require, "github.com/gorilla/mux") {
|
||||||
|
language.Frameworks = append(language.Frameworks, "Mux")
|
||||||
|
}
|
||||||
|
}
|
||||||
23
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/java/java_detector.go
generated
vendored
Normal file
23
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/java/java_detector.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2021 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 recognizer
|
||||||
|
|
||||||
|
import (
|
||||||
|
utils "github.com/redhat-developer/alizer/go/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func hasFramework(configFile string, tag string) (bool, error) {
|
||||||
|
if utils.IsPathOfWantedFile(configFile, "build.gradle") {
|
||||||
|
return utils.IsTagInFile(configFile, tag)
|
||||||
|
} else {
|
||||||
|
return utils.IsTagInPomXMLFile(configFile, tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
23
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/java/micronaut_detector.go
generated
vendored
Normal file
23
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/java/micronaut_detector.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2021 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 recognizer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/redhat-developer/alizer/go/pkg/apis/language"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MicronautDetector struct{}
|
||||||
|
|
||||||
|
func (m MicronautDetector) DoFrameworkDetection(language *language.Language, config string) {
|
||||||
|
if hasFwk, _ := hasFramework(config, "io.micronaut"); hasFwk {
|
||||||
|
language.Frameworks = append(language.Frameworks, "Micronaut")
|
||||||
|
}
|
||||||
|
}
|
||||||
23
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/java/openliberty_detector.go
generated
vendored
Normal file
23
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/java/openliberty_detector.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2021 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 recognizer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/redhat-developer/alizer/go/pkg/apis/language"
|
||||||
|
)
|
||||||
|
|
||||||
|
type OpenLibertyDetector struct{}
|
||||||
|
|
||||||
|
func (o OpenLibertyDetector) DoFrameworkDetection(language *language.Language, config string) {
|
||||||
|
if hasFwk, _ := hasFramework(config, "io.openliberty"); hasFwk {
|
||||||
|
language.Frameworks = append(language.Frameworks, "OpenLiberty")
|
||||||
|
}
|
||||||
|
}
|
||||||
23
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/java/quarkus_detector.go
generated
vendored
Normal file
23
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/java/quarkus_detector.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2021 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 recognizer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/redhat-developer/alizer/go/pkg/apis/language"
|
||||||
|
)
|
||||||
|
|
||||||
|
type QuarkusDetector struct{}
|
||||||
|
|
||||||
|
func (q QuarkusDetector) DoFrameworkDetection(language *language.Language, config string) {
|
||||||
|
if hasFwk, _ := hasFramework(config, "io.quarkus"); hasFwk {
|
||||||
|
language.Frameworks = append(language.Frameworks, "Quarkus")
|
||||||
|
}
|
||||||
|
}
|
||||||
23
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/java/spring_detector.go
generated
vendored
Normal file
23
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/java/spring_detector.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2021 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 recognizer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/redhat-developer/alizer/go/pkg/apis/language"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SpringDetector struct{}
|
||||||
|
|
||||||
|
func (s SpringDetector) DoFrameworkDetection(language *language.Language, config string) {
|
||||||
|
if hasFwk, _ := hasFramework(config, "org.springframework"); hasFwk {
|
||||||
|
language.Frameworks = append(language.Frameworks, "Spring")
|
||||||
|
}
|
||||||
|
}
|
||||||
23
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/java/vertx_detector.go
generated
vendored
Normal file
23
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/java/vertx_detector.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2021 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 recognizer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/redhat-developer/alizer/go/pkg/apis/language"
|
||||||
|
)
|
||||||
|
|
||||||
|
type VertxDetector struct{}
|
||||||
|
|
||||||
|
func (v VertxDetector) DoFrameworkDetection(language *language.Language, config string) {
|
||||||
|
if hasFwk, _ := hasFramework(config, "io.vertx"); hasFwk {
|
||||||
|
language.Frameworks = append(language.Frameworks, "Vertx")
|
||||||
|
}
|
||||||
|
}
|
||||||
23
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/javascript/nodejs/express_detector.go
generated
vendored
Normal file
23
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/javascript/nodejs/express_detector.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2021 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 recognizer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/redhat-developer/alizer/go/pkg/apis/language"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ExpressDetector struct{}
|
||||||
|
|
||||||
|
func (e ExpressDetector) DoFrameworkDetection(language *language.Language, config string) {
|
||||||
|
if hasFramework(config, "express") {
|
||||||
|
language.Frameworks = append(language.Frameworks, "Express")
|
||||||
|
}
|
||||||
|
}
|
||||||
19
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/javascript/nodejs/nodejs_detector.go
generated
vendored
Normal file
19
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/javascript/nodejs/nodejs_detector.go
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2021 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 recognizer
|
||||||
|
|
||||||
|
import (
|
||||||
|
utils "github.com/redhat-developer/alizer/go/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func hasFramework(configFile string, tag string) bool {
|
||||||
|
return utils.IsTagInPackageJsonFile(configFile, tag)
|
||||||
|
}
|
||||||
23
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/javascript/nodejs/reactjs_detector.go
generated
vendored
Normal file
23
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/javascript/nodejs/reactjs_detector.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2022 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 recognizer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/redhat-developer/alizer/go/pkg/apis/language"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ReactJsDetector struct{}
|
||||||
|
|
||||||
|
func (r ReactJsDetector) DoFrameworkDetection(language *language.Language, config string) {
|
||||||
|
if hasFramework(config, "react") {
|
||||||
|
language.Frameworks = append(language.Frameworks, "React")
|
||||||
|
}
|
||||||
|
}
|
||||||
35
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/python/django_detector.go
generated
vendored
Normal file
35
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/python/django_detector.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2021 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 recognizer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/redhat-developer/alizer/go/pkg/apis/language"
|
||||||
|
"github.com/redhat-developer/alizer/go/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DjangoDetector struct{}
|
||||||
|
|
||||||
|
func (d DjangoDetector) DoFrameworkDetection(language *language.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")
|
||||||
|
|
||||||
|
djangoFiles := []string{}
|
||||||
|
utils.AddToArrayIfValueExist(&djangoFiles, managePy)
|
||||||
|
utils.AddToArrayIfValueExist(&djangoFiles, urlsPy)
|
||||||
|
utils.AddToArrayIfValueExist(&djangoFiles, wsgiPy)
|
||||||
|
utils.AddToArrayIfValueExist(&djangoFiles, asgiPy)
|
||||||
|
|
||||||
|
if hasFramework(&djangoFiles, "from django.") {
|
||||||
|
language.Frameworks = append(language.Frameworks, "Django")
|
||||||
|
}
|
||||||
|
}
|
||||||
24
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/python/python_detector.go
generated
vendored
Normal file
24
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/python/python_detector.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2021 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 recognizer
|
||||||
|
|
||||||
|
import (
|
||||||
|
utils "github.com/redhat-developer/alizer/go/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func hasFramework(files *[]string, tag string) bool {
|
||||||
|
for _, file := range *files {
|
||||||
|
if hasTag, _ := utils.IsTagInFile(file, tag); hasTag {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
73
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/go_enricher.go
generated
vendored
Normal file
73
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/go_enricher.go
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2022 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 recognizer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
|
framework "github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go"
|
||||||
|
"github.com/redhat-developer/alizer/go/pkg/apis/language"
|
||||||
|
utils "github.com/redhat-developer/alizer/go/pkg/utils"
|
||||||
|
"golang.org/x/mod/modfile"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GoEnricher struct{}
|
||||||
|
|
||||||
|
type GoFrameworkDetector interface {
|
||||||
|
DoFrameworkDetection(language *language.Language, goMod *modfile.File)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGoFrameworkDetectors() []GoFrameworkDetector {
|
||||||
|
return []GoFrameworkDetector{
|
||||||
|
&framework.GinDetector{},
|
||||||
|
&framework.BeegoDetector{},
|
||||||
|
&framework.EchoDetector{},
|
||||||
|
&framework.FastHttpDetector{},
|
||||||
|
&framework.GoFiberDetector{},
|
||||||
|
&framework.MuxDetector{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j GoEnricher) GetSupportedLanguages() []string {
|
||||||
|
return []string{"go"}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j GoEnricher) DoEnrichLanguage(language *language.Language, files *[]string) {
|
||||||
|
goModPath := utils.GetFile(files, "go.mod")
|
||||||
|
|
||||||
|
if goModPath != "" {
|
||||||
|
goModFile, err := getGoModFile(goModPath)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
language.Tools = []string{goModFile.Go.Version}
|
||||||
|
detectGoFrameworks(language, goModFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j GoEnricher) IsConfigValidForComponentDetection(language string, config string) bool {
|
||||||
|
return IsConfigurationValidForLanguage(language, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGoModFile(filePath string) (*modfile.File, error) {
|
||||||
|
b, err := ioutil.ReadFile(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("unable to read go.mod file")
|
||||||
|
}
|
||||||
|
return modfile.Parse(filePath, b, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectGoFrameworks(language *language.Language, configFile *modfile.File) {
|
||||||
|
for _, detector := range getGoFrameworkDetectors() {
|
||||||
|
detector.DoFrameworkDetection(language, configFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
79
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/java_enricher.go
generated
vendored
Normal file
79
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/java_enricher.go
generated
vendored
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2021 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 recognizer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
framework "github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/java"
|
||||||
|
"github.com/redhat-developer/alizer/go/pkg/apis/language"
|
||||||
|
utils "github.com/redhat-developer/alizer/go/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type JavaEnricher struct{}
|
||||||
|
|
||||||
|
func getJavaFrameworkDetectors() []FrameworkDetectorWithConfigFile {
|
||||||
|
return []FrameworkDetectorWithConfigFile{
|
||||||
|
&framework.MicronautDetector{},
|
||||||
|
&framework.OpenLibertyDetector{},
|
||||||
|
&framework.QuarkusDetector{},
|
||||||
|
&framework.SpringDetector{},
|
||||||
|
&framework.VertxDetector{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j JavaEnricher) GetSupportedLanguages() []string {
|
||||||
|
return []string{"java"}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j JavaEnricher) DoEnrichLanguage(language *language.Language, files *[]string) {
|
||||||
|
gradle := utils.GetFile(files, "build.gradle")
|
||||||
|
maven := utils.GetFile(files, "pom.xml")
|
||||||
|
ant := utils.GetFile(files, "build.xml")
|
||||||
|
|
||||||
|
if gradle != "" {
|
||||||
|
language.Tools = []string{"Gradle"}
|
||||||
|
detectJavaFrameworks(language, gradle)
|
||||||
|
} else if maven != "" {
|
||||||
|
language.Tools = []string{"Maven"}
|
||||||
|
detectJavaFrameworks(language, maven)
|
||||||
|
} else if ant != "" {
|
||||||
|
language.Tools = []string{"Ant"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j JavaEnricher) IsConfigValidForComponentDetection(language string, config string) bool {
|
||||||
|
return IsConfigurationValidForLanguage(language, config) && !isParentModuleMaven(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
isParentModuleMaven checks if configuration file is a parent pom.xml
|
||||||
|
Parameters:
|
||||||
|
configPath: configuration file path
|
||||||
|
Returns:
|
||||||
|
bool: true if config file is parent
|
||||||
|
*/
|
||||||
|
func isParentModuleMaven(configPath string) bool {
|
||||||
|
_, file := filepath.Split(configPath)
|
||||||
|
if !strings.EqualFold(file, "pom.xml") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
hasTag, _ := utils.IsTagInPomXMLFile(configPath, "modules")
|
||||||
|
return hasTag
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectJavaFrameworks(language *language.Language, configFile string) {
|
||||||
|
for _, detector := range getJavaFrameworkDetectors() {
|
||||||
|
detector.DoFrameworkDetection(language, configFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
49
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/javascript_enricher.go
generated
vendored
Normal file
49
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/javascript_enricher.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2021 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 recognizer
|
||||||
|
|
||||||
|
import (
|
||||||
|
framework "github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/javascript/nodejs"
|
||||||
|
"github.com/redhat-developer/alizer/go/pkg/apis/language"
|
||||||
|
utils "github.com/redhat-developer/alizer/go/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type JavaScriptEnricher struct{}
|
||||||
|
|
||||||
|
func getJavaScriptFrameworkDetectors() []FrameworkDetectorWithConfigFile {
|
||||||
|
return []FrameworkDetectorWithConfigFile{
|
||||||
|
&framework.ExpressDetector{},
|
||||||
|
&framework.ReactJsDetector{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j JavaScriptEnricher) GetSupportedLanguages() []string {
|
||||||
|
return []string{"javascript", "typescript"}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j JavaScriptEnricher) DoEnrichLanguage(language *language.Language, files *[]string) {
|
||||||
|
packageJson := utils.GetFile(files, "package.json")
|
||||||
|
|
||||||
|
if packageJson != "" {
|
||||||
|
language.Tools = []string{"NodeJs"}
|
||||||
|
detectJavaScriptFrameworks(language, packageJson)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j JavaScriptEnricher) IsConfigValidForComponentDetection(language string, config string) bool {
|
||||||
|
return IsConfigurationValidForLanguage(language, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectJavaScriptFrameworks(language *language.Language, configFile string) {
|
||||||
|
for _, detector := range getJavaScriptFrameworkDetectors() {
|
||||||
|
detector.DoFrameworkDetection(language, configFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
43
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/python_enricher.go
generated
vendored
Normal file
43
vendor/github.com/redhat-developer/alizer/go/pkg/apis/enricher/python_enricher.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2021 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 recognizer
|
||||||
|
|
||||||
|
import (
|
||||||
|
framework "github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/python"
|
||||||
|
"github.com/redhat-developer/alizer/go/pkg/apis/language"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PythonEnricher struct{}
|
||||||
|
|
||||||
|
func getPythonFrameworkDetectors() []FrameworkDetectorWithoutConfigFile {
|
||||||
|
return []FrameworkDetectorWithoutConfigFile{
|
||||||
|
&framework.DjangoDetector{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p PythonEnricher) GetSupportedLanguages() []string {
|
||||||
|
return []string{"python"}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p PythonEnricher) DoEnrichLanguage(language *language.Language, files *[]string) {
|
||||||
|
language.Tools = []string{}
|
||||||
|
detectPythonFrameworks(language, files)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p PythonEnricher) IsConfigValidForComponentDetection(language string, config string) bool {
|
||||||
|
return IsConfigurationValidForLanguage(language, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectPythonFrameworks(language *language.Language, files *[]string) {
|
||||||
|
for _, detector := range getPythonFrameworkDetectors() {
|
||||||
|
detector.DoFrameworkDetection(language, files)
|
||||||
|
}
|
||||||
|
}
|
||||||
20
vendor/github.com/redhat-developer/alizer/go/pkg/apis/language/language.go
generated
vendored
Normal file
20
vendor/github.com/redhat-developer/alizer/go/pkg/apis/language/language.go
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2021 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 language
|
||||||
|
|
||||||
|
type Language struct {
|
||||||
|
Name string
|
||||||
|
Aliases []string
|
||||||
|
UsageInPercentage float64
|
||||||
|
Frameworks []string
|
||||||
|
Tools []string
|
||||||
|
CanBeComponent bool
|
||||||
|
}
|
||||||
248
vendor/github.com/redhat-developer/alizer/go/pkg/apis/recognizer/component_recognizer.go
generated
vendored
Normal file
248
vendor/github.com/redhat-developer/alizer/go/pkg/apis/recognizer/component_recognizer.go
generated
vendored
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2022 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 recognizer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/fs"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
enricher "github.com/redhat-developer/alizer/go/pkg/apis/enricher"
|
||||||
|
"github.com/redhat-developer/alizer/go/pkg/apis/language"
|
||||||
|
"github.com/redhat-developer/alizer/go/pkg/utils/langfiles"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Component struct {
|
||||||
|
Path string
|
||||||
|
Languages []language.Language
|
||||||
|
}
|
||||||
|
|
||||||
|
func DetectComponents(path string) ([]Component, error) {
|
||||||
|
files, err := getFilePaths(path)
|
||||||
|
if err != nil {
|
||||||
|
return []Component{}, err
|
||||||
|
}
|
||||||
|
components, err := detectComponents(files)
|
||||||
|
if err != nil {
|
||||||
|
return []Component{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// it may happen that a language has no a specific configuration file (e.g opposite to JAVA -> pom.xml and Nodejs -> package.json)
|
||||||
|
// we then rely on the language recognizer
|
||||||
|
directoriesNotBelongingToExistingComponent := getDirectoriesWithoutConfigFile(path, components)
|
||||||
|
components = append(components, getComponentsWithoutConfigFile(directoriesNotBelongingToExistingComponent)...)
|
||||||
|
|
||||||
|
return components, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
getComponentsWithoutConfigFile retrieves the components which are written with a language that does not require a config file
|
||||||
|
Parameters:
|
||||||
|
directories: list of directories to analyze
|
||||||
|
Returns:
|
||||||
|
components found
|
||||||
|
*/
|
||||||
|
func getComponentsWithoutConfigFile(directories []string) []Component {
|
||||||
|
var components []Component
|
||||||
|
for _, dir := range directories {
|
||||||
|
component, _ := detectComponent(dir, "")
|
||||||
|
if component.Path != "" && isLangForNoConfigComponent(component.Languages) {
|
||||||
|
components = append(components, component)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return components
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
isLangForNoConfigComponent verify if main language requires any config file
|
||||||
|
Parameters:
|
||||||
|
component:
|
||||||
|
Returns:
|
||||||
|
bool: true if language does not require any config file
|
||||||
|
*/
|
||||||
|
func isLangForNoConfigComponent(languages []language.Language) bool {
|
||||||
|
if len(languages) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
lang, err := langfiles.Get().GetLanguageByNameOrAlias(languages[0].Name)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(lang.ConfigurationFiles) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
getDirectoriesPathsWithoutConfigFile retrieves all directories that do not contain any Component
|
||||||
|
Parameters:
|
||||||
|
root: root folder where to start the search
|
||||||
|
components: list of components already detected
|
||||||
|
Returns:
|
||||||
|
list of directories path that does not contain any component
|
||||||
|
*/
|
||||||
|
func getDirectoriesWithoutConfigFile(root string, components []Component) []string {
|
||||||
|
if len(components) == 0 {
|
||||||
|
return []string{root}
|
||||||
|
}
|
||||||
|
directories := []string{}
|
||||||
|
err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
|
||||||
|
if !strings.EqualFold(root, path) && d.IsDir() && !isAnyComponentInPath(path, components) {
|
||||||
|
directories = getParentFolders(path, directories)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
return directories
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return all paths which are not sub-folders of some other path within the list
|
||||||
|
* Target will be added to the list if it is not a sub-folder of any other path within the list
|
||||||
|
* If a path in the list is sub-folder of Target, that path will be removed.
|
||||||
|
*
|
||||||
|
* @param target new path to be added
|
||||||
|
* @param directories list of all previously added paths
|
||||||
|
* @return the list containing all paths which are not sub-folders of any other
|
||||||
|
*/
|
||||||
|
func getParentFolders(target string, directories []string) []string {
|
||||||
|
updatedDirectories := []string{}
|
||||||
|
for _, dir := range directories {
|
||||||
|
if isFirstPathParentOfSecond(dir, target) {
|
||||||
|
return directories
|
||||||
|
}
|
||||||
|
|
||||||
|
if isFirstPathParentOfSecond(target, dir) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
updatedDirectories = append(updatedDirectories, dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedDirectories = append(updatedDirectories, target)
|
||||||
|
return updatedDirectories
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
isAnyComponentInPath checks if a component is present in path
|
||||||
|
Parameters:
|
||||||
|
path: path where to search for component
|
||||||
|
components: list of components
|
||||||
|
Returns:
|
||||||
|
true if a component is found starting from path
|
||||||
|
*/
|
||||||
|
func isAnyComponentInPath(path string, components []Component) bool {
|
||||||
|
for _, component := range components {
|
||||||
|
if strings.EqualFold(path, component.Path) || isFirstPathParentOfSecond(component.Path, path) || isFirstPathParentOfSecond(path, component.Path) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
isFirstPathParentOfSecond check if first path is parent (direct or not) of second path
|
||||||
|
Parameters:
|
||||||
|
firstPath: path to be used as parent
|
||||||
|
secondPath: path to be used as child
|
||||||
|
Returns:
|
||||||
|
true if firstPath is part of secondPath
|
||||||
|
*/
|
||||||
|
func isFirstPathParentOfSecond(firstPath string, secondPath string) bool {
|
||||||
|
return strings.Contains(secondPath, firstPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
detectComponents detect components by analyzing all files
|
||||||
|
Parameters:
|
||||||
|
files: list of files to analyze
|
||||||
|
Returns:
|
||||||
|
list of components detected or err if any error occurs
|
||||||
|
*/
|
||||||
|
func detectComponents(files []string) ([]Component, error) {
|
||||||
|
configurationPerLanguage := langfiles.Get().GetConfigurationPerLanguageMapping()
|
||||||
|
var components []Component
|
||||||
|
for _, file := range files {
|
||||||
|
dir, fileName := filepath.Split(file)
|
||||||
|
if language, isConfig := configurationPerLanguage[fileName]; isConfig && isConfigurationValid(language, file) {
|
||||||
|
component, err := detectComponent(dir, language)
|
||||||
|
if err != nil {
|
||||||
|
return []Component{}, err
|
||||||
|
}
|
||||||
|
if component.Path != "" {
|
||||||
|
components = append(components, component)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return components, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
detectComponent returns a Component if found:
|
||||||
|
- language must be enabled for component detection
|
||||||
|
- there should be at least one framework detected
|
||||||
|
, error otherwise
|
||||||
|
Parameters:
|
||||||
|
root: path to be used as root where to start the detection
|
||||||
|
language: language to be used as target for detection
|
||||||
|
Returns:
|
||||||
|
component detected or error if any error occurs
|
||||||
|
*/
|
||||||
|
func detectComponent(root string, language string) (Component, error) {
|
||||||
|
languages, err := Analyze(root)
|
||||||
|
if err != nil {
|
||||||
|
return Component{}, err
|
||||||
|
}
|
||||||
|
languages = getLanguagesWeightedByConfigFile(languages, language)
|
||||||
|
if len(languages) > 0 {
|
||||||
|
if mainLang := languages[0]; mainLang.CanBeComponent && len(mainLang.Frameworks) > 0 {
|
||||||
|
return Component{
|
||||||
|
Path: root,
|
||||||
|
Languages: languages,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Component{}, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
getLanguagesWeightedByConfigFile returns the list of languages reordered by importance per config file.
|
||||||
|
Language found by analyzing the config file is used as target.
|
||||||
|
Parameters:
|
||||||
|
languages: list of languages to be reordered
|
||||||
|
languageByConfig: target language
|
||||||
|
Returns:
|
||||||
|
list of languages reordered
|
||||||
|
*/
|
||||||
|
func getLanguagesWeightedByConfigFile(languages []language.Language, languageByConfig string) []language.Language {
|
||||||
|
if languageByConfig == "" {
|
||||||
|
return languages
|
||||||
|
}
|
||||||
|
|
||||||
|
for index, lang := range languages {
|
||||||
|
if strings.EqualFold(lang.Name, languageByConfig) {
|
||||||
|
sliceWithoutLang := append(languages[:index], languages[index+1:]...)
|
||||||
|
return append([]language.Language{lang}, sliceWithoutLang...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return languages
|
||||||
|
}
|
||||||
|
|
||||||
|
func isConfigurationValid(language string, file string) bool {
|
||||||
|
langEnricher := enricher.GetEnricherByLanguage(language)
|
||||||
|
if langEnricher != nil {
|
||||||
|
return langEnricher.IsConfigValidForComponentDetection(language, file)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
89
vendor/github.com/redhat-developer/alizer/go/pkg/apis/recognizer/devfile_recognizer.go
generated
vendored
Normal file
89
vendor/github.com/redhat-developer/alizer/go/pkg/apis/recognizer/devfile_recognizer.go
generated
vendored
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2022 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 recognizer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/redhat-developer/alizer/go/pkg/apis/language"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DevFileType struct {
|
||||||
|
Name string
|
||||||
|
Language string
|
||||||
|
ProjectType string
|
||||||
|
Tags []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func SelectDevFileFromTypes(path string, devFileTypes []DevFileType) (DevFileType, error) {
|
||||||
|
languages, err := Analyze(path)
|
||||||
|
if err != nil {
|
||||||
|
return DevFileType{}, err
|
||||||
|
}
|
||||||
|
devfile, err := SelectDevFileUsingLanguagesFromTypes(languages, devFileTypes)
|
||||||
|
if err != nil {
|
||||||
|
return DevFileType{}, errors.New("No valid devfile found for project in " + path)
|
||||||
|
}
|
||||||
|
return devfile, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SelectDevFileUsingLanguagesFromTypes(languages []language.Language, devFileTypes []DevFileType) (DevFileType, error) {
|
||||||
|
for _, language := range languages {
|
||||||
|
devfile, err := selectDevFileByLanguage(language, devFileTypes)
|
||||||
|
if err == nil {
|
||||||
|
return devfile, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return DevFileType{}, errors.New("no valid devfile found by using those languages")
|
||||||
|
}
|
||||||
|
|
||||||
|
func selectDevFileByLanguage(language language.Language, devFileTypes []DevFileType) (DevFileType, error) {
|
||||||
|
scoreTarget := 0
|
||||||
|
devfileTarget := DevFileType{}
|
||||||
|
FRAMEWORK_WEIGHT := 10
|
||||||
|
TOOL_WEIGHT := 5
|
||||||
|
for _, devFile := range devFileTypes {
|
||||||
|
score := 0
|
||||||
|
if strings.EqualFold(devFile.Language, language.Name) || matches(language.Aliases, devFile.Language) {
|
||||||
|
score++
|
||||||
|
if matches(language.Frameworks, devFile.ProjectType) {
|
||||||
|
score += FRAMEWORK_WEIGHT
|
||||||
|
}
|
||||||
|
for _, tag := range devFile.Tags {
|
||||||
|
if matches(language.Frameworks, tag) {
|
||||||
|
score += FRAMEWORK_WEIGHT
|
||||||
|
}
|
||||||
|
if matches(language.Tools, tag) {
|
||||||
|
score += TOOL_WEIGHT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if score > scoreTarget {
|
||||||
|
scoreTarget = score
|
||||||
|
devfileTarget = devFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if scoreTarget == 0 {
|
||||||
|
return devfileTarget, errors.New("No valid devfile found for current language " + language.Name)
|
||||||
|
}
|
||||||
|
return devfileTarget, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func matches(values []string, valueToFind string) bool {
|
||||||
|
for _, value := range values {
|
||||||
|
if strings.EqualFold(value, valueToFind) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
117
vendor/github.com/redhat-developer/alizer/go/pkg/apis/recognizer/language_recognizer.go
generated
vendored
Normal file
117
vendor/github.com/redhat-developer/alizer/go/pkg/apis/recognizer/language_recognizer.go
generated
vendored
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2021 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 recognizer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
enricher "github.com/redhat-developer/alizer/go/pkg/apis/enricher"
|
||||||
|
"github.com/redhat-developer/alizer/go/pkg/apis/language"
|
||||||
|
langfile "github.com/redhat-developer/alizer/go/pkg/utils/langfiles"
|
||||||
|
)
|
||||||
|
|
||||||
|
type languageItem struct {
|
||||||
|
item langfile.LanguageItem
|
||||||
|
percentage int
|
||||||
|
}
|
||||||
|
|
||||||
|
func Analyze(path string) ([]language.Language, error) {
|
||||||
|
languagesFile := langfile.Get()
|
||||||
|
languagesDetected := make(map[string]languageItem)
|
||||||
|
|
||||||
|
paths, err := getFilePaths(path)
|
||||||
|
if err != nil {
|
||||||
|
return []language.Language{}, err
|
||||||
|
}
|
||||||
|
extensionsGrouped := extractExtensions(paths)
|
||||||
|
extensionHasProgrammingLanguage := false
|
||||||
|
totalProgrammingOccurrences := 0
|
||||||
|
for extension := range extensionsGrouped {
|
||||||
|
languages := languagesFile.GetLanguagesByExtension(extension)
|
||||||
|
if len(languages) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, language := range languages {
|
||||||
|
if language.Kind == "programming" {
|
||||||
|
var languageFileItem langfile.LanguageItem
|
||||||
|
var err error
|
||||||
|
if len(language.Group) == 0 {
|
||||||
|
languageFileItem = language
|
||||||
|
} else {
|
||||||
|
languageFileItem, err = languagesFile.GetLanguageByName(language.Group)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tmpLanguageItem := languageItem{languageFileItem, 0}
|
||||||
|
percentage := languagesDetected[tmpLanguageItem.item.Name].percentage + extensionsGrouped[extension]
|
||||||
|
tmpLanguageItem.percentage = percentage
|
||||||
|
languagesDetected[tmpLanguageItem.item.Name] = tmpLanguageItem
|
||||||
|
extensionHasProgrammingLanguage = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if extensionHasProgrammingLanguage {
|
||||||
|
totalProgrammingOccurrences += extensionsGrouped[extension]
|
||||||
|
extensionHasProgrammingLanguage = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var languagesFound []language.Language
|
||||||
|
for name, item := range languagesDetected {
|
||||||
|
tmpPercentage := float64(item.percentage) / float64(totalProgrammingOccurrences)
|
||||||
|
tmpPercentage = float64(int(tmpPercentage*10000)) / 10000
|
||||||
|
if tmpPercentage > 0.02 {
|
||||||
|
tmpLanguage := language.Language{
|
||||||
|
Name: name,
|
||||||
|
Aliases: item.item.Aliases,
|
||||||
|
UsageInPercentage: tmpPercentage * 100,
|
||||||
|
Frameworks: []string{},
|
||||||
|
Tools: []string{},
|
||||||
|
CanBeComponent: item.item.Component}
|
||||||
|
langEnricher := enricher.GetEnricherByLanguage(name)
|
||||||
|
if langEnricher != nil {
|
||||||
|
langEnricher.DoEnrichLanguage(&tmpLanguage, &paths)
|
||||||
|
}
|
||||||
|
languagesFound = append(languagesFound, tmpLanguage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.SliceStable(languagesFound, func(i, j int) bool {
|
||||||
|
return languagesFound[i].UsageInPercentage > languagesFound[j].UsageInPercentage
|
||||||
|
})
|
||||||
|
|
||||||
|
return languagesFound, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractExtensions(paths []string) map[string]int {
|
||||||
|
extensions := make(map[string]int)
|
||||||
|
for _, path := range paths {
|
||||||
|
extension := filepath.Ext(path)
|
||||||
|
if len(extension) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
count := extensions[extension] + 1
|
||||||
|
extensions[extension] = count
|
||||||
|
}
|
||||||
|
return extensions
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFilePaths(root string) ([]string, error) {
|
||||||
|
var files []string
|
||||||
|
err := filepath.Walk(root,
|
||||||
|
func(path string, info os.FileInfo, err error) error {
|
||||||
|
files = append(files, path)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return files, err
|
||||||
|
}
|
||||||
19
vendor/github.com/redhat-developer/alizer/go/pkg/schema/dotnet_proj.go
generated
vendored
Normal file
19
vendor/github.com/redhat-developer/alizer/go/pkg/schema/dotnet_proj.go
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2022 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 DotNetProject struct {
|
||||||
|
PropertyGroup struct {
|
||||||
|
TargetFramework string `xml:"TargetFramework"`
|
||||||
|
TargetFrameworkVersion string `xml:"TargetFrameworkVersion"`
|
||||||
|
TargetFrameworks string `xml:"TargetFrameworks"`
|
||||||
|
} `xml:"PropertyGroup"`
|
||||||
|
}
|
||||||
35
vendor/github.com/redhat-developer/alizer/go/pkg/schema/languages.go
generated
vendored
Normal file
35
vendor/github.com/redhat-developer/alizer/go/pkg/schema/languages.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2021 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 LanguageProperties struct {
|
||||||
|
Type string `yaml:"type,omitempty"`
|
||||||
|
Color string `yaml:"color,omitempty"`
|
||||||
|
Extensions []string `yaml:"extensions,omitempty"`
|
||||||
|
TmScope string `yaml:"tm_scope,omitempty"`
|
||||||
|
AceMode string `yaml:"ace_mode,omitempty"`
|
||||||
|
LanguageID int `yaml:"language_id,omitempty"`
|
||||||
|
Aliases []string `yaml:"aliases,omitempty"`
|
||||||
|
CodemirrorMode string `yaml:"codemirror_mode,omitempty"`
|
||||||
|
CodemirrorMimeType string `yaml:"codemirror_mime_type,omitempty"`
|
||||||
|
Group string `yaml:"group"`
|
||||||
|
Filenames []string `yaml:"filenames"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LanguagesProperties map[string]LanguageProperties
|
||||||
|
|
||||||
|
type LanguageCustomization struct {
|
||||||
|
ConfigurationFiles []string `yaml:"configuration_files"`
|
||||||
|
Component bool `yaml:"component"`
|
||||||
|
ExcludeFolders []string `yaml:"exclude_folders,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LanguagesCustomizations map[string]LanguageCustomization
|
||||||
15
vendor/github.com/redhat-developer/alizer/go/pkg/schema/package_json.go
generated
vendored
Normal file
15
vendor/github.com/redhat-developer/alizer/go/pkg/schema/package_json.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2021 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 PackageJson struct {
|
||||||
|
Dependencies map[string]string `json:"dependencies"`
|
||||||
|
}
|
||||||
27
vendor/github.com/redhat-developer/alizer/go/pkg/schema/pom_xml.go
generated
vendored
Normal file
27
vendor/github.com/redhat-developer/alizer/go/pkg/schema/pom_xml.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2021 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 Pom struct {
|
||||||
|
Dependencies struct {
|
||||||
|
Text string `xml:",chardata"`
|
||||||
|
Dependency []struct {
|
||||||
|
Text string `xml:",chardata"`
|
||||||
|
GroupId string `xml:"groupId"`
|
||||||
|
ArtifactId string `xml:"artifactId"`
|
||||||
|
Version string `xml:"version"`
|
||||||
|
Scope string `xml:"scope"`
|
||||||
|
} `xml:"dependency"`
|
||||||
|
} `xml:"dependencies"`
|
||||||
|
Modules struct {
|
||||||
|
Module string `xml:"module"`
|
||||||
|
} `xml:"modules,omitempty"`
|
||||||
|
}
|
||||||
135
vendor/github.com/redhat-developer/alizer/go/pkg/utils/detector.go
generated
vendored
Normal file
135
vendor/github.com/redhat-developer/alizer/go/pkg/utils/detector.go
generated
vendored
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2021 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 utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"encoding/xml"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/redhat-developer/alizer/go/pkg/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetFilesByRegex(filePaths *[]string, regexFile string) []string {
|
||||||
|
matchedPaths := []string{}
|
||||||
|
for _, path := range *filePaths {
|
||||||
|
if isPathOfWantedRegex(path, regexFile) {
|
||||||
|
matchedPaths = append(matchedPaths, path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matchedPaths
|
||||||
|
}
|
||||||
|
|
||||||
|
func isPathOfWantedRegex(path string, regexFile string) bool {
|
||||||
|
_, file := filepath.Split(path)
|
||||||
|
matched, _ := regexp.MatchString(regexFile, file)
|
||||||
|
return matched
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetFile(filePaths *[]string, wantedFile string) string {
|
||||||
|
for _, path := range *filePaths {
|
||||||
|
if IsPathOfWantedFile(path, wantedFile) {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func HasFile(files *[]string, wantedFile string) bool {
|
||||||
|
for _, path := range *files {
|
||||||
|
if IsPathOfWantedFile(path, wantedFile) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsPathOfWantedFile(path string, wantedFile string) bool {
|
||||||
|
_, file := filepath.Split(path)
|
||||||
|
return strings.EqualFold(file, wantedFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsTagInFile(file string, tag string) (bool, error) {
|
||||||
|
contentInByte, err := ioutil.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
content := string(contentInByte)
|
||||||
|
return strings.Contains(content, tag), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsTagInPomXMLFile(pomFilePath string, tag string) (bool, error) {
|
||||||
|
pom, err := GetPomFileContent(pomFilePath)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
for _, dependency := range pom.Dependencies.Dependency {
|
||||||
|
if strings.Contains(dependency.GroupId, tag) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPomFileContent(pomFilePath string) (schema.Pom, error) {
|
||||||
|
xmlFile, err := os.Open(pomFilePath)
|
||||||
|
if err != nil {
|
||||||
|
return schema.Pom{}, err
|
||||||
|
}
|
||||||
|
byteValue, _ := ioutil.ReadAll(xmlFile)
|
||||||
|
|
||||||
|
var pom schema.Pom
|
||||||
|
xml.Unmarshal(byteValue, &pom)
|
||||||
|
|
||||||
|
defer xmlFile.Close()
|
||||||
|
return pom, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsTagInPackageJsonFile(file string, tag string) bool {
|
||||||
|
jsonFile, err := os.Open(file)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
byteValue, _ := ioutil.ReadAll(jsonFile)
|
||||||
|
|
||||||
|
var packageJson schema.PackageJson
|
||||||
|
json.Unmarshal(byteValue, &packageJson)
|
||||||
|
|
||||||
|
defer jsonFile.Close()
|
||||||
|
if packageJson.Dependencies != nil {
|
||||||
|
for dependency := range packageJson.Dependencies {
|
||||||
|
if strings.Contains(dependency, tag) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddToArrayIfValueExist(arr *[]string, val string) {
|
||||||
|
if val != "" {
|
||||||
|
*arr = append(*arr, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Contains(s []string, str string) bool {
|
||||||
|
for _, v := range s {
|
||||||
|
if v == str {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
152
vendor/github.com/redhat-developer/alizer/go/pkg/utils/langfiles/languages_file_handler.go
generated
vendored
Normal file
152
vendor/github.com/redhat-developer/alizer/go/pkg/utils/langfiles/languages_file_handler.go
generated
vendored
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2021 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 langfiles
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/redhat-developer/alizer/go/pkg/schema"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LanguageItem struct {
|
||||||
|
Name string
|
||||||
|
Aliases []string
|
||||||
|
Kind string
|
||||||
|
Group string
|
||||||
|
ConfigurationFiles []string
|
||||||
|
ExcludeFolders []string
|
||||||
|
Component bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type LanguageFile struct {
|
||||||
|
languages map[string]LanguageItem
|
||||||
|
extensionsXLanguage map[string][]LanguageItem
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
instance *LanguageFile
|
||||||
|
|
||||||
|
//go:embed resources
|
||||||
|
res embed.FS
|
||||||
|
)
|
||||||
|
|
||||||
|
func Get() *LanguageFile {
|
||||||
|
if instance == nil {
|
||||||
|
instance = create()
|
||||||
|
}
|
||||||
|
return instance
|
||||||
|
}
|
||||||
|
|
||||||
|
func create() *LanguageFile {
|
||||||
|
languages := make(map[string]LanguageItem)
|
||||||
|
extensionsXLanguage := make(map[string][]LanguageItem)
|
||||||
|
|
||||||
|
languagesProperties := getLanguagesProperties()
|
||||||
|
|
||||||
|
for name, properties := range languagesProperties {
|
||||||
|
languageItem := LanguageItem{
|
||||||
|
Name: name,
|
||||||
|
Aliases: properties.Aliases,
|
||||||
|
Kind: properties.Type,
|
||||||
|
Group: properties.Group,
|
||||||
|
}
|
||||||
|
customizeLanguage(&languageItem)
|
||||||
|
languages[name] = languageItem
|
||||||
|
extensions := properties.Extensions
|
||||||
|
for _, ext := range extensions {
|
||||||
|
languagesByExtension := extensionsXLanguage[ext]
|
||||||
|
languagesByExtension = append(languagesByExtension, languageItem)
|
||||||
|
extensionsXLanguage[ext] = languagesByExtension
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &LanguageFile{
|
||||||
|
languages: languages,
|
||||||
|
extensionsXLanguage: extensionsXLanguage,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func customizeLanguage(languageItem *LanguageItem) {
|
||||||
|
languagesCustomizations := getLanguageCustomizations()
|
||||||
|
if customization, hasCustomization := languagesCustomizations[(*languageItem).Name]; hasCustomization {
|
||||||
|
(*languageItem).ConfigurationFiles = customization.ConfigurationFiles
|
||||||
|
(*languageItem).ExcludeFolders = customization.ExcludeFolders
|
||||||
|
(*languageItem).Component = customization.Component
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLanguagesProperties() schema.LanguagesProperties {
|
||||||
|
yamlFile, err := res.ReadFile("resources/languages.yml")
|
||||||
|
if err != nil {
|
||||||
|
return schema.LanguagesProperties{}
|
||||||
|
}
|
||||||
|
var data schema.LanguagesProperties
|
||||||
|
yaml.Unmarshal(yamlFile, &data)
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLanguageCustomizations() schema.LanguagesCustomizations {
|
||||||
|
yamlFile, err := res.ReadFile("resources/languages-customization.yml")
|
||||||
|
if err != nil {
|
||||||
|
return schema.LanguagesCustomizations{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var data schema.LanguagesCustomizations
|
||||||
|
yaml.Unmarshal(yamlFile, &data)
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LanguageFile) GetLanguagesByExtension(extension string) []LanguageItem {
|
||||||
|
return l.extensionsXLanguage[extension]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LanguageFile) GetLanguageByName(name string) (LanguageItem, error) {
|
||||||
|
for langName, langItem := range l.languages {
|
||||||
|
if langName == name {
|
||||||
|
return langItem, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return LanguageItem{}, errors.New("no language found with this name")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LanguageFile) GetLanguageByAlias(alias string) (LanguageItem, error) {
|
||||||
|
for _, langItem := range l.languages {
|
||||||
|
for _, aliasItem := range langItem.Aliases {
|
||||||
|
if strings.EqualFold(alias, aliasItem) {
|
||||||
|
return langItem, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return LanguageItem{}, errors.New("no language found with this alias")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LanguageFile) GetLanguageByNameOrAlias(name string) (LanguageItem, error) {
|
||||||
|
langItem, err := l.GetLanguageByName(name)
|
||||||
|
if err == nil {
|
||||||
|
return langItem, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return l.GetLanguageByAlias(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LanguageFile) GetConfigurationPerLanguageMapping() map[string]string {
|
||||||
|
configurationPerLanguage := make(map[string]string)
|
||||||
|
for langName, langItem := range l.languages {
|
||||||
|
configurationFiles := langItem.ConfigurationFiles
|
||||||
|
for _, configFile := range configurationFiles {
|
||||||
|
configurationPerLanguage[configFile] = langName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return configurationPerLanguage
|
||||||
|
}
|
||||||
43
vendor/github.com/redhat-developer/alizer/go/pkg/utils/langfiles/resources/languages-customization.yml
generated
vendored
Normal file
43
vendor/github.com/redhat-developer/alizer/go/pkg/utils/langfiles/resources/languages-customization.yml
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
C#:
|
||||||
|
aliases:
|
||||||
|
- "dotnet"
|
||||||
|
configuration_files:
|
||||||
|
- ".*\\.\\w+proj"
|
||||||
|
- "appsettings.json"
|
||||||
|
component: true
|
||||||
|
F#:
|
||||||
|
aliases:
|
||||||
|
- "dotnet"
|
||||||
|
configuration_files:
|
||||||
|
- ".*\\.\\w+proj"
|
||||||
|
- "appsettings.json"
|
||||||
|
component: true
|
||||||
|
Go:
|
||||||
|
configuration_files:
|
||||||
|
- "go.mod"
|
||||||
|
- "go.sum"
|
||||||
|
component: true
|
||||||
|
Java:
|
||||||
|
configuration_files:
|
||||||
|
- "pom.xml"
|
||||||
|
- "build.gradle"
|
||||||
|
component: true
|
||||||
|
JavaScript:
|
||||||
|
exclude_folders:
|
||||||
|
- "node_modules"
|
||||||
|
configuration_files:
|
||||||
|
- "package.json"
|
||||||
|
component: true
|
||||||
|
Python:
|
||||||
|
component: true
|
||||||
|
Rust:
|
||||||
|
configuration_files:
|
||||||
|
- "Cargo.toml"
|
||||||
|
component: true
|
||||||
|
Visual Basic .NET:
|
||||||
|
aliases:
|
||||||
|
- "dotnet"
|
||||||
|
configuration_files:
|
||||||
|
- ".*\\.\\w+proj"
|
||||||
|
- "appsettings.json"
|
||||||
|
component: true
|
||||||
6623
vendor/github.com/redhat-developer/alizer/go/pkg/utils/langfiles/resources/languages.yml
generated
vendored
Normal file
6623
vendor/github.com/redhat-developer/alizer/go/pkg/utils/langfiles/resources/languages.yml
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
78
vendor/golang.org/x/mod/internal/lazyregexp/lazyre.go
generated
vendored
Normal file
78
vendor/golang.org/x/mod/internal/lazyregexp/lazyre.go
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
// Copyright 2018 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package lazyregexp is a thin wrapper over regexp, allowing the use of global
|
||||||
|
// regexp variables without forcing them to be compiled at init.
|
||||||
|
package lazyregexp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Regexp is a wrapper around regexp.Regexp, where the underlying regexp will be
|
||||||
|
// compiled the first time it is needed.
|
||||||
|
type Regexp struct {
|
||||||
|
str string
|
||||||
|
once sync.Once
|
||||||
|
rx *regexp.Regexp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Regexp) re() *regexp.Regexp {
|
||||||
|
r.once.Do(r.build)
|
||||||
|
return r.rx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Regexp) build() {
|
||||||
|
r.rx = regexp.MustCompile(r.str)
|
||||||
|
r.str = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Regexp) FindSubmatch(s []byte) [][]byte {
|
||||||
|
return r.re().FindSubmatch(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Regexp) FindStringSubmatch(s string) []string {
|
||||||
|
return r.re().FindStringSubmatch(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Regexp) FindStringSubmatchIndex(s string) []int {
|
||||||
|
return r.re().FindStringSubmatchIndex(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Regexp) ReplaceAllString(src, repl string) string {
|
||||||
|
return r.re().ReplaceAllString(src, repl)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Regexp) FindString(s string) string {
|
||||||
|
return r.re().FindString(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Regexp) FindAllString(s string, n int) []string {
|
||||||
|
return r.re().FindAllString(s, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Regexp) MatchString(s string) bool {
|
||||||
|
return r.re().MatchString(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Regexp) SubexpNames() []string {
|
||||||
|
return r.re().SubexpNames()
|
||||||
|
}
|
||||||
|
|
||||||
|
var inTest = len(os.Args) > 0 && strings.HasSuffix(strings.TrimSuffix(os.Args[0], ".exe"), ".test")
|
||||||
|
|
||||||
|
// New creates a new lazy regexp, delaying the compiling work until it is first
|
||||||
|
// needed. If the code is being run as part of tests, the regexp compiling will
|
||||||
|
// happen immediately.
|
||||||
|
func New(str string) *Regexp {
|
||||||
|
lr := &Regexp{str: str}
|
||||||
|
if inTest {
|
||||||
|
// In tests, always compile the regexps early.
|
||||||
|
lr.re()
|
||||||
|
}
|
||||||
|
return lr
|
||||||
|
}
|
||||||
174
vendor/golang.org/x/mod/modfile/print.go
generated
vendored
Normal file
174
vendor/golang.org/x/mod/modfile/print.go
generated
vendored
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
// Copyright 2018 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Module file printer.
|
||||||
|
|
||||||
|
package modfile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Format returns a go.mod file as a byte slice, formatted in standard style.
|
||||||
|
func Format(f *FileSyntax) []byte {
|
||||||
|
pr := &printer{}
|
||||||
|
pr.file(f)
|
||||||
|
return pr.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// A printer collects the state during printing of a file or expression.
|
||||||
|
type printer struct {
|
||||||
|
bytes.Buffer // output buffer
|
||||||
|
comment []Comment // pending end-of-line comments
|
||||||
|
margin int // left margin (indent), a number of tabs
|
||||||
|
}
|
||||||
|
|
||||||
|
// printf prints to the buffer.
|
||||||
|
func (p *printer) printf(format string, args ...interface{}) {
|
||||||
|
fmt.Fprintf(p, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// indent returns the position on the current line, in bytes, 0-indexed.
|
||||||
|
func (p *printer) indent() int {
|
||||||
|
b := p.Bytes()
|
||||||
|
n := 0
|
||||||
|
for n < len(b) && b[len(b)-1-n] != '\n' {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// newline ends the current line, flushing end-of-line comments.
|
||||||
|
func (p *printer) newline() {
|
||||||
|
if len(p.comment) > 0 {
|
||||||
|
p.printf(" ")
|
||||||
|
for i, com := range p.comment {
|
||||||
|
if i > 0 {
|
||||||
|
p.trim()
|
||||||
|
p.printf("\n")
|
||||||
|
for i := 0; i < p.margin; i++ {
|
||||||
|
p.printf("\t")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.printf("%s", strings.TrimSpace(com.Token))
|
||||||
|
}
|
||||||
|
p.comment = p.comment[:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
p.trim()
|
||||||
|
p.printf("\n")
|
||||||
|
for i := 0; i < p.margin; i++ {
|
||||||
|
p.printf("\t")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// trim removes trailing spaces and tabs from the current line.
|
||||||
|
func (p *printer) trim() {
|
||||||
|
// Remove trailing spaces and tabs from line we're about to end.
|
||||||
|
b := p.Bytes()
|
||||||
|
n := len(b)
|
||||||
|
for n > 0 && (b[n-1] == '\t' || b[n-1] == ' ') {
|
||||||
|
n--
|
||||||
|
}
|
||||||
|
p.Truncate(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// file formats the given file into the print buffer.
|
||||||
|
func (p *printer) file(f *FileSyntax) {
|
||||||
|
for _, com := range f.Before {
|
||||||
|
p.printf("%s", strings.TrimSpace(com.Token))
|
||||||
|
p.newline()
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, stmt := range f.Stmt {
|
||||||
|
switch x := stmt.(type) {
|
||||||
|
case *CommentBlock:
|
||||||
|
// comments already handled
|
||||||
|
p.expr(x)
|
||||||
|
|
||||||
|
default:
|
||||||
|
p.expr(x)
|
||||||
|
p.newline()
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, com := range stmt.Comment().After {
|
||||||
|
p.printf("%s", strings.TrimSpace(com.Token))
|
||||||
|
p.newline()
|
||||||
|
}
|
||||||
|
|
||||||
|
if i+1 < len(f.Stmt) {
|
||||||
|
p.newline()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *printer) expr(x Expr) {
|
||||||
|
// Emit line-comments preceding this expression.
|
||||||
|
if before := x.Comment().Before; len(before) > 0 {
|
||||||
|
// Want to print a line comment.
|
||||||
|
// Line comments must be at the current margin.
|
||||||
|
p.trim()
|
||||||
|
if p.indent() > 0 {
|
||||||
|
// There's other text on the line. Start a new line.
|
||||||
|
p.printf("\n")
|
||||||
|
}
|
||||||
|
// Re-indent to margin.
|
||||||
|
for i := 0; i < p.margin; i++ {
|
||||||
|
p.printf("\t")
|
||||||
|
}
|
||||||
|
for _, com := range before {
|
||||||
|
p.printf("%s", strings.TrimSpace(com.Token))
|
||||||
|
p.newline()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch x := x.(type) {
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("printer: unexpected type %T", x))
|
||||||
|
|
||||||
|
case *CommentBlock:
|
||||||
|
// done
|
||||||
|
|
||||||
|
case *LParen:
|
||||||
|
p.printf("(")
|
||||||
|
case *RParen:
|
||||||
|
p.printf(")")
|
||||||
|
|
||||||
|
case *Line:
|
||||||
|
p.tokens(x.Token)
|
||||||
|
|
||||||
|
case *LineBlock:
|
||||||
|
p.tokens(x.Token)
|
||||||
|
p.printf(" ")
|
||||||
|
p.expr(&x.LParen)
|
||||||
|
p.margin++
|
||||||
|
for _, l := range x.Line {
|
||||||
|
p.newline()
|
||||||
|
p.expr(l)
|
||||||
|
}
|
||||||
|
p.margin--
|
||||||
|
p.newline()
|
||||||
|
p.expr(&x.RParen)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Queue end-of-line comments for printing when we
|
||||||
|
// reach the end of the line.
|
||||||
|
p.comment = append(p.comment, x.Comment().Suffix...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *printer) tokens(tokens []string) {
|
||||||
|
sep := ""
|
||||||
|
for _, t := range tokens {
|
||||||
|
if t == "," || t == ")" || t == "]" || t == "}" {
|
||||||
|
sep = ""
|
||||||
|
}
|
||||||
|
p.printf("%s%s", sep, t)
|
||||||
|
sep = " "
|
||||||
|
if t == "(" || t == "[" || t == "{" {
|
||||||
|
sep = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
959
vendor/golang.org/x/mod/modfile/read.go
generated
vendored
Normal file
959
vendor/golang.org/x/mod/modfile/read.go
generated
vendored
Normal file
@@ -0,0 +1,959 @@
|
|||||||
|
// Copyright 2018 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package modfile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Position describes an arbitrary source position in a file, including the
|
||||||
|
// file, line, column, and byte offset.
|
||||||
|
type Position struct {
|
||||||
|
Line int // line in input (starting at 1)
|
||||||
|
LineRune int // rune in line (starting at 1)
|
||||||
|
Byte int // byte in input (starting at 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// add returns the position at the end of s, assuming it starts at p.
|
||||||
|
func (p Position) add(s string) Position {
|
||||||
|
p.Byte += len(s)
|
||||||
|
if n := strings.Count(s, "\n"); n > 0 {
|
||||||
|
p.Line += n
|
||||||
|
s = s[strings.LastIndex(s, "\n")+1:]
|
||||||
|
p.LineRune = 1
|
||||||
|
}
|
||||||
|
p.LineRune += utf8.RuneCountInString(s)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// An Expr represents an input element.
|
||||||
|
type Expr interface {
|
||||||
|
// Span returns the start and end position of the expression,
|
||||||
|
// excluding leading or trailing comments.
|
||||||
|
Span() (start, end Position)
|
||||||
|
|
||||||
|
// Comment returns the comments attached to the expression.
|
||||||
|
// This method would normally be named 'Comments' but that
|
||||||
|
// would interfere with embedding a type of the same name.
|
||||||
|
Comment() *Comments
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Comment represents a single // comment.
|
||||||
|
type Comment struct {
|
||||||
|
Start Position
|
||||||
|
Token string // without trailing newline
|
||||||
|
Suffix bool // an end of line (not whole line) comment
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comments collects the comments associated with an expression.
|
||||||
|
type Comments struct {
|
||||||
|
Before []Comment // whole-line comments before this expression
|
||||||
|
Suffix []Comment // end-of-line comments after this expression
|
||||||
|
|
||||||
|
// For top-level expressions only, After lists whole-line
|
||||||
|
// comments following the expression.
|
||||||
|
After []Comment
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comment returns the receiver. This isn't useful by itself, but
|
||||||
|
// a Comments struct is embedded into all the expression
|
||||||
|
// implementation types, and this gives each of those a Comment
|
||||||
|
// method to satisfy the Expr interface.
|
||||||
|
func (c *Comments) Comment() *Comments {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// A FileSyntax represents an entire go.mod file.
|
||||||
|
type FileSyntax struct {
|
||||||
|
Name string // file path
|
||||||
|
Comments
|
||||||
|
Stmt []Expr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *FileSyntax) Span() (start, end Position) {
|
||||||
|
if len(x.Stmt) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
start, _ = x.Stmt[0].Span()
|
||||||
|
_, end = x.Stmt[len(x.Stmt)-1].Span()
|
||||||
|
return start, end
|
||||||
|
}
|
||||||
|
|
||||||
|
// addLine adds a line containing the given tokens to the file.
|
||||||
|
//
|
||||||
|
// If the first token of the hint matches the first token of the
|
||||||
|
// line, the new line is added at the end of the block containing hint,
|
||||||
|
// extracting hint into a new block if it is not yet in one.
|
||||||
|
//
|
||||||
|
// If the hint is non-nil buts its first token does not match,
|
||||||
|
// the new line is added after the block containing hint
|
||||||
|
// (or hint itself, if not in a block).
|
||||||
|
//
|
||||||
|
// If no hint is provided, addLine appends the line to the end of
|
||||||
|
// the last block with a matching first token,
|
||||||
|
// or to the end of the file if no such block exists.
|
||||||
|
func (x *FileSyntax) addLine(hint Expr, tokens ...string) *Line {
|
||||||
|
if hint == nil {
|
||||||
|
// If no hint given, add to the last statement of the given type.
|
||||||
|
Loop:
|
||||||
|
for i := len(x.Stmt) - 1; i >= 0; i-- {
|
||||||
|
stmt := x.Stmt[i]
|
||||||
|
switch stmt := stmt.(type) {
|
||||||
|
case *Line:
|
||||||
|
if stmt.Token != nil && stmt.Token[0] == tokens[0] {
|
||||||
|
hint = stmt
|
||||||
|
break Loop
|
||||||
|
}
|
||||||
|
case *LineBlock:
|
||||||
|
if stmt.Token[0] == tokens[0] {
|
||||||
|
hint = stmt
|
||||||
|
break Loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newLineAfter := func(i int) *Line {
|
||||||
|
new := &Line{Token: tokens}
|
||||||
|
if i == len(x.Stmt) {
|
||||||
|
x.Stmt = append(x.Stmt, new)
|
||||||
|
} else {
|
||||||
|
x.Stmt = append(x.Stmt, nil)
|
||||||
|
copy(x.Stmt[i+2:], x.Stmt[i+1:])
|
||||||
|
x.Stmt[i+1] = new
|
||||||
|
}
|
||||||
|
return new
|
||||||
|
}
|
||||||
|
|
||||||
|
if hint != nil {
|
||||||
|
for i, stmt := range x.Stmt {
|
||||||
|
switch stmt := stmt.(type) {
|
||||||
|
case *Line:
|
||||||
|
if stmt == hint {
|
||||||
|
if stmt.Token == nil || stmt.Token[0] != tokens[0] {
|
||||||
|
return newLineAfter(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert line to line block.
|
||||||
|
stmt.InBlock = true
|
||||||
|
block := &LineBlock{Token: stmt.Token[:1], Line: []*Line{stmt}}
|
||||||
|
stmt.Token = stmt.Token[1:]
|
||||||
|
x.Stmt[i] = block
|
||||||
|
new := &Line{Token: tokens[1:], InBlock: true}
|
||||||
|
block.Line = append(block.Line, new)
|
||||||
|
return new
|
||||||
|
}
|
||||||
|
|
||||||
|
case *LineBlock:
|
||||||
|
if stmt == hint {
|
||||||
|
if stmt.Token[0] != tokens[0] {
|
||||||
|
return newLineAfter(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
new := &Line{Token: tokens[1:], InBlock: true}
|
||||||
|
stmt.Line = append(stmt.Line, new)
|
||||||
|
return new
|
||||||
|
}
|
||||||
|
|
||||||
|
for j, line := range stmt.Line {
|
||||||
|
if line == hint {
|
||||||
|
if stmt.Token[0] != tokens[0] {
|
||||||
|
return newLineAfter(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add new line after hint within the block.
|
||||||
|
stmt.Line = append(stmt.Line, nil)
|
||||||
|
copy(stmt.Line[j+2:], stmt.Line[j+1:])
|
||||||
|
new := &Line{Token: tokens[1:], InBlock: true}
|
||||||
|
stmt.Line[j+1] = new
|
||||||
|
return new
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new := &Line{Token: tokens}
|
||||||
|
x.Stmt = append(x.Stmt, new)
|
||||||
|
return new
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *FileSyntax) updateLine(line *Line, tokens ...string) {
|
||||||
|
if line.InBlock {
|
||||||
|
tokens = tokens[1:]
|
||||||
|
}
|
||||||
|
line.Token = tokens
|
||||||
|
}
|
||||||
|
|
||||||
|
// markRemoved modifies line so that it (and its end-of-line comment, if any)
|
||||||
|
// will be dropped by (*FileSyntax).Cleanup.
|
||||||
|
func (line *Line) markRemoved() {
|
||||||
|
line.Token = nil
|
||||||
|
line.Comments.Suffix = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup cleans up the file syntax x after any edit operations.
|
||||||
|
// To avoid quadratic behavior, (*Line).markRemoved marks the line as dead
|
||||||
|
// by setting line.Token = nil but does not remove it from the slice
|
||||||
|
// in which it appears. After edits have all been indicated,
|
||||||
|
// calling Cleanup cleans out the dead lines.
|
||||||
|
func (x *FileSyntax) Cleanup() {
|
||||||
|
w := 0
|
||||||
|
for _, stmt := range x.Stmt {
|
||||||
|
switch stmt := stmt.(type) {
|
||||||
|
case *Line:
|
||||||
|
if stmt.Token == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
case *LineBlock:
|
||||||
|
ww := 0
|
||||||
|
for _, line := range stmt.Line {
|
||||||
|
if line.Token != nil {
|
||||||
|
stmt.Line[ww] = line
|
||||||
|
ww++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ww == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ww == 1 {
|
||||||
|
// Collapse block into single line.
|
||||||
|
line := &Line{
|
||||||
|
Comments: Comments{
|
||||||
|
Before: commentsAdd(stmt.Before, stmt.Line[0].Before),
|
||||||
|
Suffix: commentsAdd(stmt.Line[0].Suffix, stmt.Suffix),
|
||||||
|
After: commentsAdd(stmt.Line[0].After, stmt.After),
|
||||||
|
},
|
||||||
|
Token: stringsAdd(stmt.Token, stmt.Line[0].Token),
|
||||||
|
}
|
||||||
|
x.Stmt[w] = line
|
||||||
|
w++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
stmt.Line = stmt.Line[:ww]
|
||||||
|
}
|
||||||
|
x.Stmt[w] = stmt
|
||||||
|
w++
|
||||||
|
}
|
||||||
|
x.Stmt = x.Stmt[:w]
|
||||||
|
}
|
||||||
|
|
||||||
|
func commentsAdd(x, y []Comment) []Comment {
|
||||||
|
return append(x[:len(x):len(x)], y...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringsAdd(x, y []string) []string {
|
||||||
|
return append(x[:len(x):len(x)], y...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A CommentBlock represents a top-level block of comments separate
|
||||||
|
// from any rule.
|
||||||
|
type CommentBlock struct {
|
||||||
|
Comments
|
||||||
|
Start Position
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CommentBlock) Span() (start, end Position) {
|
||||||
|
return x.Start, x.Start
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Line is a single line of tokens.
|
||||||
|
type Line struct {
|
||||||
|
Comments
|
||||||
|
Start Position
|
||||||
|
Token []string
|
||||||
|
InBlock bool
|
||||||
|
End Position
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Line) Span() (start, end Position) {
|
||||||
|
return x.Start, x.End
|
||||||
|
}
|
||||||
|
|
||||||
|
// A LineBlock is a factored block of lines, like
|
||||||
|
//
|
||||||
|
// require (
|
||||||
|
// "x"
|
||||||
|
// "y"
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
type LineBlock struct {
|
||||||
|
Comments
|
||||||
|
Start Position
|
||||||
|
LParen LParen
|
||||||
|
Token []string
|
||||||
|
Line []*Line
|
||||||
|
RParen RParen
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LineBlock) Span() (start, end Position) {
|
||||||
|
return x.Start, x.RParen.Pos.add(")")
|
||||||
|
}
|
||||||
|
|
||||||
|
// An LParen represents the beginning of a parenthesized line block.
|
||||||
|
// It is a place to store suffix comments.
|
||||||
|
type LParen struct {
|
||||||
|
Comments
|
||||||
|
Pos Position
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LParen) Span() (start, end Position) {
|
||||||
|
return x.Pos, x.Pos.add(")")
|
||||||
|
}
|
||||||
|
|
||||||
|
// An RParen represents the end of a parenthesized line block.
|
||||||
|
// It is a place to store whole-line (before) comments.
|
||||||
|
type RParen struct {
|
||||||
|
Comments
|
||||||
|
Pos Position
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *RParen) Span() (start, end Position) {
|
||||||
|
return x.Pos, x.Pos.add(")")
|
||||||
|
}
|
||||||
|
|
||||||
|
// An input represents a single input file being parsed.
|
||||||
|
type input struct {
|
||||||
|
// Lexing state.
|
||||||
|
filename string // name of input file, for errors
|
||||||
|
complete []byte // entire input
|
||||||
|
remaining []byte // remaining input
|
||||||
|
tokenStart []byte // token being scanned to end of input
|
||||||
|
token token // next token to be returned by lex, peek
|
||||||
|
pos Position // current input position
|
||||||
|
comments []Comment // accumulated comments
|
||||||
|
|
||||||
|
// Parser state.
|
||||||
|
file *FileSyntax // returned top-level syntax tree
|
||||||
|
parseErrors ErrorList // errors encountered during parsing
|
||||||
|
|
||||||
|
// Comment assignment state.
|
||||||
|
pre []Expr // all expressions, in preorder traversal
|
||||||
|
post []Expr // all expressions, in postorder traversal
|
||||||
|
}
|
||||||
|
|
||||||
|
func newInput(filename string, data []byte) *input {
|
||||||
|
return &input{
|
||||||
|
filename: filename,
|
||||||
|
complete: data,
|
||||||
|
remaining: data,
|
||||||
|
pos: Position{Line: 1, LineRune: 1, Byte: 0},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse parses the input file.
|
||||||
|
func parse(file string, data []byte) (f *FileSyntax, err error) {
|
||||||
|
// The parser panics for both routine errors like syntax errors
|
||||||
|
// and for programmer bugs like array index errors.
|
||||||
|
// Turn both into error returns. Catching bug panics is
|
||||||
|
// especially important when processing many files.
|
||||||
|
in := newInput(file, data)
|
||||||
|
defer func() {
|
||||||
|
if e := recover(); e != nil && e != &in.parseErrors {
|
||||||
|
in.parseErrors = append(in.parseErrors, Error{
|
||||||
|
Filename: in.filename,
|
||||||
|
Pos: in.pos,
|
||||||
|
Err: fmt.Errorf("internal error: %v", e),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if err == nil && len(in.parseErrors) > 0 {
|
||||||
|
err = in.parseErrors
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Prime the lexer by reading in the first token. It will be available
|
||||||
|
// in the next peek() or lex() call.
|
||||||
|
in.readToken()
|
||||||
|
|
||||||
|
// Invoke the parser.
|
||||||
|
in.parseFile()
|
||||||
|
if len(in.parseErrors) > 0 {
|
||||||
|
return nil, in.parseErrors
|
||||||
|
}
|
||||||
|
in.file.Name = in.filename
|
||||||
|
|
||||||
|
// Assign comments to nearby syntax.
|
||||||
|
in.assignComments()
|
||||||
|
|
||||||
|
return in.file, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error is called to report an error.
|
||||||
|
// Error does not return: it panics.
|
||||||
|
func (in *input) Error(s string) {
|
||||||
|
in.parseErrors = append(in.parseErrors, Error{
|
||||||
|
Filename: in.filename,
|
||||||
|
Pos: in.pos,
|
||||||
|
Err: errors.New(s),
|
||||||
|
})
|
||||||
|
panic(&in.parseErrors)
|
||||||
|
}
|
||||||
|
|
||||||
|
// eof reports whether the input has reached end of file.
|
||||||
|
func (in *input) eof() bool {
|
||||||
|
return len(in.remaining) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// peekRune returns the next rune in the input without consuming it.
|
||||||
|
func (in *input) peekRune() int {
|
||||||
|
if len(in.remaining) == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
r, _ := utf8.DecodeRune(in.remaining)
|
||||||
|
return int(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// peekPrefix reports whether the remaining input begins with the given prefix.
|
||||||
|
func (in *input) peekPrefix(prefix string) bool {
|
||||||
|
// This is like bytes.HasPrefix(in.remaining, []byte(prefix))
|
||||||
|
// but without the allocation of the []byte copy of prefix.
|
||||||
|
for i := 0; i < len(prefix); i++ {
|
||||||
|
if i >= len(in.remaining) || in.remaining[i] != prefix[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// readRune consumes and returns the next rune in the input.
|
||||||
|
func (in *input) readRune() int {
|
||||||
|
if len(in.remaining) == 0 {
|
||||||
|
in.Error("internal lexer error: readRune at EOF")
|
||||||
|
}
|
||||||
|
r, size := utf8.DecodeRune(in.remaining)
|
||||||
|
in.remaining = in.remaining[size:]
|
||||||
|
if r == '\n' {
|
||||||
|
in.pos.Line++
|
||||||
|
in.pos.LineRune = 1
|
||||||
|
} else {
|
||||||
|
in.pos.LineRune++
|
||||||
|
}
|
||||||
|
in.pos.Byte += size
|
||||||
|
return int(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
type token struct {
|
||||||
|
kind tokenKind
|
||||||
|
pos Position
|
||||||
|
endPos Position
|
||||||
|
text string
|
||||||
|
}
|
||||||
|
|
||||||
|
type tokenKind int
|
||||||
|
|
||||||
|
const (
|
||||||
|
_EOF tokenKind = -(iota + 1)
|
||||||
|
_EOLCOMMENT
|
||||||
|
_IDENT
|
||||||
|
_STRING
|
||||||
|
_COMMENT
|
||||||
|
|
||||||
|
// newlines and punctuation tokens are allowed as ASCII codes.
|
||||||
|
)
|
||||||
|
|
||||||
|
func (k tokenKind) isComment() bool {
|
||||||
|
return k == _COMMENT || k == _EOLCOMMENT
|
||||||
|
}
|
||||||
|
|
||||||
|
// isEOL returns whether a token terminates a line.
|
||||||
|
func (k tokenKind) isEOL() bool {
|
||||||
|
return k == _EOF || k == _EOLCOMMENT || k == '\n'
|
||||||
|
}
|
||||||
|
|
||||||
|
// startToken marks the beginning of the next input token.
|
||||||
|
// It must be followed by a call to endToken, once the token's text has
|
||||||
|
// been consumed using readRune.
|
||||||
|
func (in *input) startToken() {
|
||||||
|
in.tokenStart = in.remaining
|
||||||
|
in.token.text = ""
|
||||||
|
in.token.pos = in.pos
|
||||||
|
}
|
||||||
|
|
||||||
|
// endToken marks the end of an input token.
|
||||||
|
// It records the actual token string in tok.text.
|
||||||
|
// A single trailing newline (LF or CRLF) will be removed from comment tokens.
|
||||||
|
func (in *input) endToken(kind tokenKind) {
|
||||||
|
in.token.kind = kind
|
||||||
|
text := string(in.tokenStart[:len(in.tokenStart)-len(in.remaining)])
|
||||||
|
if kind.isComment() {
|
||||||
|
if strings.HasSuffix(text, "\r\n") {
|
||||||
|
text = text[:len(text)-2]
|
||||||
|
} else {
|
||||||
|
text = strings.TrimSuffix(text, "\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
in.token.text = text
|
||||||
|
in.token.endPos = in.pos
|
||||||
|
}
|
||||||
|
|
||||||
|
// peek returns the kind of the the next token returned by lex.
|
||||||
|
func (in *input) peek() tokenKind {
|
||||||
|
return in.token.kind
|
||||||
|
}
|
||||||
|
|
||||||
|
// lex is called from the parser to obtain the next input token.
|
||||||
|
func (in *input) lex() token {
|
||||||
|
tok := in.token
|
||||||
|
in.readToken()
|
||||||
|
return tok
|
||||||
|
}
|
||||||
|
|
||||||
|
// readToken lexes the next token from the text and stores it in in.token.
|
||||||
|
func (in *input) readToken() {
|
||||||
|
// Skip past spaces, stopping at non-space or EOF.
|
||||||
|
for !in.eof() {
|
||||||
|
c := in.peekRune()
|
||||||
|
if c == ' ' || c == '\t' || c == '\r' {
|
||||||
|
in.readRune()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comment runs to end of line.
|
||||||
|
if in.peekPrefix("//") {
|
||||||
|
in.startToken()
|
||||||
|
|
||||||
|
// Is this comment the only thing on its line?
|
||||||
|
// Find the last \n before this // and see if it's all
|
||||||
|
// spaces from there to here.
|
||||||
|
i := bytes.LastIndex(in.complete[:in.pos.Byte], []byte("\n"))
|
||||||
|
suffix := len(bytes.TrimSpace(in.complete[i+1:in.pos.Byte])) > 0
|
||||||
|
in.readRune()
|
||||||
|
in.readRune()
|
||||||
|
|
||||||
|
// Consume comment.
|
||||||
|
for len(in.remaining) > 0 && in.readRune() != '\n' {
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are at top level (not in a statement), hand the comment to
|
||||||
|
// the parser as a _COMMENT token. The grammar is written
|
||||||
|
// to handle top-level comments itself.
|
||||||
|
if !suffix {
|
||||||
|
in.endToken(_COMMENT)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, save comment for later attachment to syntax tree.
|
||||||
|
in.endToken(_EOLCOMMENT)
|
||||||
|
in.comments = append(in.comments, Comment{in.token.pos, in.token.text, suffix})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if in.peekPrefix("/*") {
|
||||||
|
in.Error("mod files must use // comments (not /* */ comments)")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Found non-space non-comment.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Found the beginning of the next token.
|
||||||
|
in.startToken()
|
||||||
|
|
||||||
|
// End of file.
|
||||||
|
if in.eof() {
|
||||||
|
in.endToken(_EOF)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Punctuation tokens.
|
||||||
|
switch c := in.peekRune(); c {
|
||||||
|
case '\n', '(', ')', '[', ']', '{', '}', ',':
|
||||||
|
in.readRune()
|
||||||
|
in.endToken(tokenKind(c))
|
||||||
|
return
|
||||||
|
|
||||||
|
case '"', '`': // quoted string
|
||||||
|
quote := c
|
||||||
|
in.readRune()
|
||||||
|
for {
|
||||||
|
if in.eof() {
|
||||||
|
in.pos = in.token.pos
|
||||||
|
in.Error("unexpected EOF in string")
|
||||||
|
}
|
||||||
|
if in.peekRune() == '\n' {
|
||||||
|
in.Error("unexpected newline in string")
|
||||||
|
}
|
||||||
|
c := in.readRune()
|
||||||
|
if c == quote {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if c == '\\' && quote != '`' {
|
||||||
|
if in.eof() {
|
||||||
|
in.pos = in.token.pos
|
||||||
|
in.Error("unexpected EOF in string")
|
||||||
|
}
|
||||||
|
in.readRune()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
in.endToken(_STRING)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checked all punctuation. Must be identifier token.
|
||||||
|
if c := in.peekRune(); !isIdent(c) {
|
||||||
|
in.Error(fmt.Sprintf("unexpected input character %#q", c))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan over identifier.
|
||||||
|
for isIdent(in.peekRune()) {
|
||||||
|
if in.peekPrefix("//") {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if in.peekPrefix("/*") {
|
||||||
|
in.Error("mod files must use // comments (not /* */ comments)")
|
||||||
|
}
|
||||||
|
in.readRune()
|
||||||
|
}
|
||||||
|
in.endToken(_IDENT)
|
||||||
|
}
|
||||||
|
|
||||||
|
// isIdent reports whether c is an identifier rune.
|
||||||
|
// We treat most printable runes as identifier runes, except for a handful of
|
||||||
|
// ASCII punctuation characters.
|
||||||
|
func isIdent(c int) bool {
|
||||||
|
switch r := rune(c); r {
|
||||||
|
case ' ', '(', ')', '[', ']', '{', '}', ',':
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
return !unicode.IsSpace(r) && unicode.IsPrint(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comment assignment.
|
||||||
|
// We build two lists of all subexpressions, preorder and postorder.
|
||||||
|
// The preorder list is ordered by start location, with outer expressions first.
|
||||||
|
// The postorder list is ordered by end location, with outer expressions last.
|
||||||
|
// We use the preorder list to assign each whole-line comment to the syntax
|
||||||
|
// immediately following it, and we use the postorder list to assign each
|
||||||
|
// end-of-line comment to the syntax immediately preceding it.
|
||||||
|
|
||||||
|
// order walks the expression adding it and its subexpressions to the
|
||||||
|
// preorder and postorder lists.
|
||||||
|
func (in *input) order(x Expr) {
|
||||||
|
if x != nil {
|
||||||
|
in.pre = append(in.pre, x)
|
||||||
|
}
|
||||||
|
switch x := x.(type) {
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("order: unexpected type %T", x))
|
||||||
|
case nil:
|
||||||
|
// nothing
|
||||||
|
case *LParen, *RParen:
|
||||||
|
// nothing
|
||||||
|
case *CommentBlock:
|
||||||
|
// nothing
|
||||||
|
case *Line:
|
||||||
|
// nothing
|
||||||
|
case *FileSyntax:
|
||||||
|
for _, stmt := range x.Stmt {
|
||||||
|
in.order(stmt)
|
||||||
|
}
|
||||||
|
case *LineBlock:
|
||||||
|
in.order(&x.LParen)
|
||||||
|
for _, l := range x.Line {
|
||||||
|
in.order(l)
|
||||||
|
}
|
||||||
|
in.order(&x.RParen)
|
||||||
|
}
|
||||||
|
if x != nil {
|
||||||
|
in.post = append(in.post, x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// assignComments attaches comments to nearby syntax.
|
||||||
|
func (in *input) assignComments() {
|
||||||
|
const debug = false
|
||||||
|
|
||||||
|
// Generate preorder and postorder lists.
|
||||||
|
in.order(in.file)
|
||||||
|
|
||||||
|
// Split into whole-line comments and suffix comments.
|
||||||
|
var line, suffix []Comment
|
||||||
|
for _, com := range in.comments {
|
||||||
|
if com.Suffix {
|
||||||
|
suffix = append(suffix, com)
|
||||||
|
} else {
|
||||||
|
line = append(line, com)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
for _, c := range line {
|
||||||
|
fmt.Fprintf(os.Stderr, "LINE %q :%d:%d #%d\n", c.Token, c.Start.Line, c.Start.LineRune, c.Start.Byte)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign line comments to syntax immediately following.
|
||||||
|
for _, x := range in.pre {
|
||||||
|
start, _ := x.Span()
|
||||||
|
if debug {
|
||||||
|
fmt.Fprintf(os.Stderr, "pre %T :%d:%d #%d\n", x, start.Line, start.LineRune, start.Byte)
|
||||||
|
}
|
||||||
|
xcom := x.Comment()
|
||||||
|
for len(line) > 0 && start.Byte >= line[0].Start.Byte {
|
||||||
|
if debug {
|
||||||
|
fmt.Fprintf(os.Stderr, "ASSIGN LINE %q #%d\n", line[0].Token, line[0].Start.Byte)
|
||||||
|
}
|
||||||
|
xcom.Before = append(xcom.Before, line[0])
|
||||||
|
line = line[1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remaining line comments go at end of file.
|
||||||
|
in.file.After = append(in.file.After, line...)
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
for _, c := range suffix {
|
||||||
|
fmt.Fprintf(os.Stderr, "SUFFIX %q :%d:%d #%d\n", c.Token, c.Start.Line, c.Start.LineRune, c.Start.Byte)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign suffix comments to syntax immediately before.
|
||||||
|
for i := len(in.post) - 1; i >= 0; i-- {
|
||||||
|
x := in.post[i]
|
||||||
|
|
||||||
|
start, end := x.Span()
|
||||||
|
if debug {
|
||||||
|
fmt.Fprintf(os.Stderr, "post %T :%d:%d #%d :%d:%d #%d\n", x, start.Line, start.LineRune, start.Byte, end.Line, end.LineRune, end.Byte)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not assign suffix comments to end of line block or whole file.
|
||||||
|
// Instead assign them to the last element inside.
|
||||||
|
switch x.(type) {
|
||||||
|
case *FileSyntax:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not assign suffix comments to something that starts
|
||||||
|
// on an earlier line, so that in
|
||||||
|
//
|
||||||
|
// x ( y
|
||||||
|
// z ) // comment
|
||||||
|
//
|
||||||
|
// we assign the comment to z and not to x ( ... ).
|
||||||
|
if start.Line != end.Line {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
xcom := x.Comment()
|
||||||
|
for len(suffix) > 0 && end.Byte <= suffix[len(suffix)-1].Start.Byte {
|
||||||
|
if debug {
|
||||||
|
fmt.Fprintf(os.Stderr, "ASSIGN SUFFIX %q #%d\n", suffix[len(suffix)-1].Token, suffix[len(suffix)-1].Start.Byte)
|
||||||
|
}
|
||||||
|
xcom.Suffix = append(xcom.Suffix, suffix[len(suffix)-1])
|
||||||
|
suffix = suffix[:len(suffix)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We assigned suffix comments in reverse.
|
||||||
|
// If multiple suffix comments were appended to the same
|
||||||
|
// expression node, they are now in reverse. Fix that.
|
||||||
|
for _, x := range in.post {
|
||||||
|
reverseComments(x.Comment().Suffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remaining suffix comments go at beginning of file.
|
||||||
|
in.file.Before = append(in.file.Before, suffix...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// reverseComments reverses the []Comment list.
|
||||||
|
func reverseComments(list []Comment) {
|
||||||
|
for i, j := 0, len(list)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
list[i], list[j] = list[j], list[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (in *input) parseFile() {
|
||||||
|
in.file = new(FileSyntax)
|
||||||
|
var cb *CommentBlock
|
||||||
|
for {
|
||||||
|
switch in.peek() {
|
||||||
|
case '\n':
|
||||||
|
in.lex()
|
||||||
|
if cb != nil {
|
||||||
|
in.file.Stmt = append(in.file.Stmt, cb)
|
||||||
|
cb = nil
|
||||||
|
}
|
||||||
|
case _COMMENT:
|
||||||
|
tok := in.lex()
|
||||||
|
if cb == nil {
|
||||||
|
cb = &CommentBlock{Start: tok.pos}
|
||||||
|
}
|
||||||
|
com := cb.Comment()
|
||||||
|
com.Before = append(com.Before, Comment{Start: tok.pos, Token: tok.text})
|
||||||
|
case _EOF:
|
||||||
|
if cb != nil {
|
||||||
|
in.file.Stmt = append(in.file.Stmt, cb)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
in.parseStmt()
|
||||||
|
if cb != nil {
|
||||||
|
in.file.Stmt[len(in.file.Stmt)-1].Comment().Before = cb.Before
|
||||||
|
cb = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (in *input) parseStmt() {
|
||||||
|
tok := in.lex()
|
||||||
|
start := tok.pos
|
||||||
|
end := tok.endPos
|
||||||
|
tokens := []string{tok.text}
|
||||||
|
for {
|
||||||
|
tok := in.lex()
|
||||||
|
switch {
|
||||||
|
case tok.kind.isEOL():
|
||||||
|
in.file.Stmt = append(in.file.Stmt, &Line{
|
||||||
|
Start: start,
|
||||||
|
Token: tokens,
|
||||||
|
End: end,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
|
||||||
|
case tok.kind == '(':
|
||||||
|
if next := in.peek(); next.isEOL() {
|
||||||
|
// Start of block: no more tokens on this line.
|
||||||
|
in.file.Stmt = append(in.file.Stmt, in.parseLineBlock(start, tokens, tok))
|
||||||
|
return
|
||||||
|
} else if next == ')' {
|
||||||
|
rparen := in.lex()
|
||||||
|
if in.peek().isEOL() {
|
||||||
|
// Empty block.
|
||||||
|
in.lex()
|
||||||
|
in.file.Stmt = append(in.file.Stmt, &LineBlock{
|
||||||
|
Start: start,
|
||||||
|
Token: tokens,
|
||||||
|
LParen: LParen{Pos: tok.pos},
|
||||||
|
RParen: RParen{Pos: rparen.pos},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// '( )' in the middle of the line, not a block.
|
||||||
|
tokens = append(tokens, tok.text, rparen.text)
|
||||||
|
} else {
|
||||||
|
// '(' in the middle of the line, not a block.
|
||||||
|
tokens = append(tokens, tok.text)
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
tokens = append(tokens, tok.text)
|
||||||
|
end = tok.endPos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (in *input) parseLineBlock(start Position, token []string, lparen token) *LineBlock {
|
||||||
|
x := &LineBlock{
|
||||||
|
Start: start,
|
||||||
|
Token: token,
|
||||||
|
LParen: LParen{Pos: lparen.pos},
|
||||||
|
}
|
||||||
|
var comments []Comment
|
||||||
|
for {
|
||||||
|
switch in.peek() {
|
||||||
|
case _EOLCOMMENT:
|
||||||
|
// Suffix comment, will be attached later by assignComments.
|
||||||
|
in.lex()
|
||||||
|
case '\n':
|
||||||
|
// Blank line. Add an empty comment to preserve it.
|
||||||
|
in.lex()
|
||||||
|
if len(comments) == 0 && len(x.Line) > 0 || len(comments) > 0 && comments[len(comments)-1].Token != "" {
|
||||||
|
comments = append(comments, Comment{})
|
||||||
|
}
|
||||||
|
case _COMMENT:
|
||||||
|
tok := in.lex()
|
||||||
|
comments = append(comments, Comment{Start: tok.pos, Token: tok.text})
|
||||||
|
case _EOF:
|
||||||
|
in.Error(fmt.Sprintf("syntax error (unterminated block started at %s:%d:%d)", in.filename, x.Start.Line, x.Start.LineRune))
|
||||||
|
case ')':
|
||||||
|
rparen := in.lex()
|
||||||
|
x.RParen.Before = comments
|
||||||
|
x.RParen.Pos = rparen.pos
|
||||||
|
if !in.peek().isEOL() {
|
||||||
|
in.Error("syntax error (expected newline after closing paren)")
|
||||||
|
}
|
||||||
|
in.lex()
|
||||||
|
return x
|
||||||
|
default:
|
||||||
|
l := in.parseLine()
|
||||||
|
x.Line = append(x.Line, l)
|
||||||
|
l.Comment().Before = comments
|
||||||
|
comments = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (in *input) parseLine() *Line {
|
||||||
|
tok := in.lex()
|
||||||
|
if tok.kind.isEOL() {
|
||||||
|
in.Error("internal parse error: parseLine at end of line")
|
||||||
|
}
|
||||||
|
start := tok.pos
|
||||||
|
end := tok.endPos
|
||||||
|
tokens := []string{tok.text}
|
||||||
|
for {
|
||||||
|
tok := in.lex()
|
||||||
|
if tok.kind.isEOL() {
|
||||||
|
return &Line{
|
||||||
|
Start: start,
|
||||||
|
Token: tokens,
|
||||||
|
End: end,
|
||||||
|
InBlock: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tokens = append(tokens, tok.text)
|
||||||
|
end = tok.endPos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
slashSlash = []byte("//")
|
||||||
|
moduleStr = []byte("module")
|
||||||
|
)
|
||||||
|
|
||||||
|
// ModulePath returns the module path from the gomod file text.
|
||||||
|
// If it cannot find a module path, it returns an empty string.
|
||||||
|
// It is tolerant of unrelated problems in the go.mod file.
|
||||||
|
func ModulePath(mod []byte) string {
|
||||||
|
for len(mod) > 0 {
|
||||||
|
line := mod
|
||||||
|
mod = nil
|
||||||
|
if i := bytes.IndexByte(line, '\n'); i >= 0 {
|
||||||
|
line, mod = line[:i], line[i+1:]
|
||||||
|
}
|
||||||
|
if i := bytes.Index(line, slashSlash); i >= 0 {
|
||||||
|
line = line[:i]
|
||||||
|
}
|
||||||
|
line = bytes.TrimSpace(line)
|
||||||
|
if !bytes.HasPrefix(line, moduleStr) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
line = line[len(moduleStr):]
|
||||||
|
n := len(line)
|
||||||
|
line = bytes.TrimSpace(line)
|
||||||
|
if len(line) == n || len(line) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if line[0] == '"' || line[0] == '`' {
|
||||||
|
p, err := strconv.Unquote(string(line))
|
||||||
|
if err != nil {
|
||||||
|
return "" // malformed quoted string or multiline module path
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(line)
|
||||||
|
}
|
||||||
|
return "" // missing module path
|
||||||
|
}
|
||||||
1468
vendor/golang.org/x/mod/modfile/rule.go
generated
vendored
Normal file
1468
vendor/golang.org/x/mod/modfile/rule.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
844
vendor/golang.org/x/mod/module/module.go
generated
vendored
Normal file
844
vendor/golang.org/x/mod/module/module.go
generated
vendored
Normal file
@@ -0,0 +1,844 @@
|
|||||||
|
// Copyright 2018 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package module defines the module.Version type along with support code.
|
||||||
|
//
|
||||||
|
// The module.Version type is a simple Path, Version pair:
|
||||||
|
//
|
||||||
|
// type Version struct {
|
||||||
|
// Path string
|
||||||
|
// Version string
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// There are no restrictions imposed directly by use of this structure,
|
||||||
|
// but additional checking functions, most notably Check, verify that
|
||||||
|
// a particular path, version pair is valid.
|
||||||
|
//
|
||||||
|
// Escaped Paths
|
||||||
|
//
|
||||||
|
// Module paths appear as substrings of file system paths
|
||||||
|
// (in the download cache) and of web server URLs in the proxy protocol.
|
||||||
|
// In general we cannot rely on file systems to be case-sensitive,
|
||||||
|
// nor can we rely on web servers, since they read from file systems.
|
||||||
|
// That is, we cannot rely on the file system to keep rsc.io/QUOTE
|
||||||
|
// and rsc.io/quote separate. Windows and macOS don't.
|
||||||
|
// Instead, we must never require two different casings of a file path.
|
||||||
|
// Because we want the download cache to match the proxy protocol,
|
||||||
|
// and because we want the proxy protocol to be possible to serve
|
||||||
|
// from a tree of static files (which might be stored on a case-insensitive
|
||||||
|
// file system), the proxy protocol must never require two different casings
|
||||||
|
// of a URL path either.
|
||||||
|
//
|
||||||
|
// One possibility would be to make the escaped form be the lowercase
|
||||||
|
// hexadecimal encoding of the actual path bytes. This would avoid ever
|
||||||
|
// needing different casings of a file path, but it would be fairly illegible
|
||||||
|
// to most programmers when those paths appeared in the file system
|
||||||
|
// (including in file paths in compiler errors and stack traces)
|
||||||
|
// in web server logs, and so on. Instead, we want a safe escaped form that
|
||||||
|
// leaves most paths unaltered.
|
||||||
|
//
|
||||||
|
// The safe escaped form is to replace every uppercase letter
|
||||||
|
// with an exclamation mark followed by the letter's lowercase equivalent.
|
||||||
|
//
|
||||||
|
// For example,
|
||||||
|
//
|
||||||
|
// github.com/Azure/azure-sdk-for-go -> github.com/!azure/azure-sdk-for-go.
|
||||||
|
// github.com/GoogleCloudPlatform/cloudsql-proxy -> github.com/!google!cloud!platform/cloudsql-proxy
|
||||||
|
// github.com/Sirupsen/logrus -> github.com/!sirupsen/logrus.
|
||||||
|
//
|
||||||
|
// Import paths that avoid upper-case letters are left unchanged.
|
||||||
|
// Note that because import paths are ASCII-only and avoid various
|
||||||
|
// problematic punctuation (like : < and >), the escaped form is also ASCII-only
|
||||||
|
// and avoids the same problematic punctuation.
|
||||||
|
//
|
||||||
|
// Import paths have never allowed exclamation marks, so there is no
|
||||||
|
// need to define how to escape a literal !.
|
||||||
|
//
|
||||||
|
// Unicode Restrictions
|
||||||
|
//
|
||||||
|
// Today, paths are disallowed from using Unicode.
|
||||||
|
//
|
||||||
|
// Although paths are currently disallowed from using Unicode,
|
||||||
|
// we would like at some point to allow Unicode letters as well, to assume that
|
||||||
|
// file systems and URLs are Unicode-safe (storing UTF-8), and apply
|
||||||
|
// the !-for-uppercase convention for escaping them in the file system.
|
||||||
|
// But there are at least two subtle considerations.
|
||||||
|
//
|
||||||
|
// First, note that not all case-fold equivalent distinct runes
|
||||||
|
// form an upper/lower pair.
|
||||||
|
// For example, U+004B ('K'), U+006B ('k'), and U+212A ('K' for Kelvin)
|
||||||
|
// are three distinct runes that case-fold to each other.
|
||||||
|
// When we do add Unicode letters, we must not assume that upper/lower
|
||||||
|
// are the only case-equivalent pairs.
|
||||||
|
// Perhaps the Kelvin symbol would be disallowed entirely, for example.
|
||||||
|
// Or perhaps it would escape as "!!k", or perhaps as "(212A)".
|
||||||
|
//
|
||||||
|
// Second, it would be nice to allow Unicode marks as well as letters,
|
||||||
|
// but marks include combining marks, and then we must deal not
|
||||||
|
// only with case folding but also normalization: both U+00E9 ('é')
|
||||||
|
// and U+0065 U+0301 ('e' followed by combining acute accent)
|
||||||
|
// look the same on the page and are treated by some file systems
|
||||||
|
// as the same path. If we do allow Unicode marks in paths, there
|
||||||
|
// must be some kind of normalization to allow only one canonical
|
||||||
|
// encoding of any character used in an import path.
|
||||||
|
package module
|
||||||
|
|
||||||
|
// IMPORTANT NOTE
|
||||||
|
//
|
||||||
|
// This file essentially defines the set of valid import paths for the go command.
|
||||||
|
// There are many subtle considerations, including Unicode ambiguity,
|
||||||
|
// security, network, and file system representations.
|
||||||
|
//
|
||||||
|
// This file also defines the set of valid module path and version combinations,
|
||||||
|
// another topic with many subtle considerations.
|
||||||
|
//
|
||||||
|
// Changes to the semantics in this file require approval from rsc.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"golang.org/x/mod/semver"
|
||||||
|
errors "golang.org/x/xerrors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Version (for clients, a module.Version) is defined by a module path and version pair.
|
||||||
|
// These are stored in their plain (unescaped) form.
|
||||||
|
type Version struct {
|
||||||
|
// Path is a module path, like "golang.org/x/text" or "rsc.io/quote/v2".
|
||||||
|
Path string
|
||||||
|
|
||||||
|
// Version is usually a semantic version in canonical form.
|
||||||
|
// There are three exceptions to this general rule.
|
||||||
|
// First, the top-level target of a build has no specific version
|
||||||
|
// and uses Version = "".
|
||||||
|
// Second, during MVS calculations the version "none" is used
|
||||||
|
// to represent the decision to take no version of a given module.
|
||||||
|
// Third, filesystem paths found in "replace" directives are
|
||||||
|
// represented by a path with an empty version.
|
||||||
|
Version string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a representation of the Version suitable for logging
|
||||||
|
// (Path@Version, or just Path if Version is empty).
|
||||||
|
func (m Version) String() string {
|
||||||
|
if m.Version == "" {
|
||||||
|
return m.Path
|
||||||
|
}
|
||||||
|
return m.Path + "@" + m.Version
|
||||||
|
}
|
||||||
|
|
||||||
|
// A ModuleError indicates an error specific to a module.
|
||||||
|
type ModuleError struct {
|
||||||
|
Path string
|
||||||
|
Version string
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// VersionError returns a ModuleError derived from a Version and error,
|
||||||
|
// or err itself if it is already such an error.
|
||||||
|
func VersionError(v Version, err error) error {
|
||||||
|
var mErr *ModuleError
|
||||||
|
if errors.As(err, &mErr) && mErr.Path == v.Path && mErr.Version == v.Version {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return &ModuleError{
|
||||||
|
Path: v.Path,
|
||||||
|
Version: v.Version,
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ModuleError) Error() string {
|
||||||
|
if v, ok := e.Err.(*InvalidVersionError); ok {
|
||||||
|
return fmt.Sprintf("%s@%s: invalid %s: %v", e.Path, v.Version, v.noun(), v.Err)
|
||||||
|
}
|
||||||
|
if e.Version != "" {
|
||||||
|
return fmt.Sprintf("%s@%s: %v", e.Path, e.Version, e.Err)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("module %s: %v", e.Path, e.Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ModuleError) Unwrap() error { return e.Err }
|
||||||
|
|
||||||
|
// An InvalidVersionError indicates an error specific to a version, with the
|
||||||
|
// module path unknown or specified externally.
|
||||||
|
//
|
||||||
|
// A ModuleError may wrap an InvalidVersionError, but an InvalidVersionError
|
||||||
|
// must not wrap a ModuleError.
|
||||||
|
type InvalidVersionError struct {
|
||||||
|
Version string
|
||||||
|
Pseudo bool
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// noun returns either "version" or "pseudo-version", depending on whether
|
||||||
|
// e.Version is a pseudo-version.
|
||||||
|
func (e *InvalidVersionError) noun() string {
|
||||||
|
if e.Pseudo {
|
||||||
|
return "pseudo-version"
|
||||||
|
}
|
||||||
|
return "version"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *InvalidVersionError) Error() string {
|
||||||
|
return fmt.Sprintf("%s %q invalid: %s", e.noun(), e.Version, e.Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *InvalidVersionError) Unwrap() error { return e.Err }
|
||||||
|
|
||||||
|
// An InvalidPathError indicates a module, import, or file path doesn't
|
||||||
|
// satisfy all naming constraints. See CheckPath, CheckImportPath,
|
||||||
|
// and CheckFilePath for specific restrictions.
|
||||||
|
type InvalidPathError struct {
|
||||||
|
Kind string // "module", "import", or "file"
|
||||||
|
Path string
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *InvalidPathError) Error() string {
|
||||||
|
return fmt.Sprintf("malformed %s path %q: %v", e.Kind, e.Path, e.Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *InvalidPathError) Unwrap() error { return e.Err }
|
||||||
|
|
||||||
|
// Check checks that a given module path, version pair is valid.
|
||||||
|
// In addition to the path being a valid module path
|
||||||
|
// and the version being a valid semantic version,
|
||||||
|
// the two must correspond.
|
||||||
|
// For example, the path "yaml/v2" only corresponds to
|
||||||
|
// semantic versions beginning with "v2.".
|
||||||
|
func Check(path, version string) error {
|
||||||
|
if err := CheckPath(path); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !semver.IsValid(version) {
|
||||||
|
return &ModuleError{
|
||||||
|
Path: path,
|
||||||
|
Err: &InvalidVersionError{Version: version, Err: errors.New("not a semantic version")},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, pathMajor, _ := SplitPathVersion(path)
|
||||||
|
if err := CheckPathMajor(version, pathMajor); err != nil {
|
||||||
|
return &ModuleError{Path: path, Err: err}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// firstPathOK reports whether r can appear in the first element of a module path.
|
||||||
|
// The first element of the path must be an LDH domain name, at least for now.
|
||||||
|
// To avoid case ambiguity, the domain name must be entirely lower case.
|
||||||
|
func firstPathOK(r rune) bool {
|
||||||
|
return r == '-' || r == '.' ||
|
||||||
|
'0' <= r && r <= '9' ||
|
||||||
|
'a' <= r && r <= 'z'
|
||||||
|
}
|
||||||
|
|
||||||
|
// modPathOK reports whether r can appear in a module path element.
|
||||||
|
// Paths can be ASCII letters, ASCII digits, and limited ASCII punctuation: - . _ and ~.
|
||||||
|
//
|
||||||
|
// This matches what "go get" has historically recognized in import paths,
|
||||||
|
// and avoids confusing sequences like '%20' or '+' that would change meaning
|
||||||
|
// if used in a URL.
|
||||||
|
//
|
||||||
|
// TODO(rsc): We would like to allow Unicode letters, but that requires additional
|
||||||
|
// care in the safe encoding (see "escaped paths" above).
|
||||||
|
func modPathOK(r rune) bool {
|
||||||
|
if r < utf8.RuneSelf {
|
||||||
|
return r == '-' || r == '.' || r == '_' || r == '~' ||
|
||||||
|
'0' <= r && r <= '9' ||
|
||||||
|
'A' <= r && r <= 'Z' ||
|
||||||
|
'a' <= r && r <= 'z'
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// modPathOK reports whether r can appear in a package import path element.
|
||||||
|
//
|
||||||
|
// Import paths are intermediate between module paths and file paths: we allow
|
||||||
|
// disallow characters that would be confusing or ambiguous as arguments to
|
||||||
|
// 'go get' (such as '@' and ' ' ), but allow certain characters that are
|
||||||
|
// otherwise-unambiguous on the command line and historically used for some
|
||||||
|
// binary names (such as '++' as a suffix for compiler binaries and wrappers).
|
||||||
|
func importPathOK(r rune) bool {
|
||||||
|
return modPathOK(r) || r == '+'
|
||||||
|
}
|
||||||
|
|
||||||
|
// fileNameOK reports whether r can appear in a file name.
|
||||||
|
// For now we allow all Unicode letters but otherwise limit to pathOK plus a few more punctuation characters.
|
||||||
|
// If we expand the set of allowed characters here, we have to
|
||||||
|
// work harder at detecting potential case-folding and normalization collisions.
|
||||||
|
// See note about "escaped paths" above.
|
||||||
|
func fileNameOK(r rune) bool {
|
||||||
|
if r < utf8.RuneSelf {
|
||||||
|
// Entire set of ASCII punctuation, from which we remove characters:
|
||||||
|
// ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~
|
||||||
|
// We disallow some shell special characters: " ' * < > ? ` |
|
||||||
|
// (Note that some of those are disallowed by the Windows file system as well.)
|
||||||
|
// We also disallow path separators / : and \ (fileNameOK is only called on path element characters).
|
||||||
|
// We allow spaces (U+0020) in file names.
|
||||||
|
const allowed = "!#$%&()+,-.=@[]^_{}~ "
|
||||||
|
if '0' <= r && r <= '9' || 'A' <= r && r <= 'Z' || 'a' <= r && r <= 'z' {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for i := 0; i < len(allowed); i++ {
|
||||||
|
if rune(allowed[i]) == r {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// It may be OK to add more ASCII punctuation here, but only carefully.
|
||||||
|
// For example Windows disallows < > \, and macOS disallows :, so we must not allow those.
|
||||||
|
return unicode.IsLetter(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckPath checks that a module path is valid.
|
||||||
|
// A valid module path is a valid import path, as checked by CheckImportPath,
|
||||||
|
// with three additional constraints.
|
||||||
|
// First, the leading path element (up to the first slash, if any),
|
||||||
|
// by convention a domain name, must contain only lower-case ASCII letters,
|
||||||
|
// ASCII digits, dots (U+002E), and dashes (U+002D);
|
||||||
|
// it must contain at least one dot and cannot start with a dash.
|
||||||
|
// Second, for a final path element of the form /vN, where N looks numeric
|
||||||
|
// (ASCII digits and dots) must not begin with a leading zero, must not be /v1,
|
||||||
|
// and must not contain any dots. For paths beginning with "gopkg.in/",
|
||||||
|
// this second requirement is replaced by a requirement that the path
|
||||||
|
// follow the gopkg.in server's conventions.
|
||||||
|
// Third, no path element may begin with a dot.
|
||||||
|
func CheckPath(path string) (err error) {
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
err = &InvalidPathError{Kind: "module", Path: path, Err: err}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err := checkPath(path, modulePath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i := strings.Index(path, "/")
|
||||||
|
if i < 0 {
|
||||||
|
i = len(path)
|
||||||
|
}
|
||||||
|
if i == 0 {
|
||||||
|
return fmt.Errorf("leading slash")
|
||||||
|
}
|
||||||
|
if !strings.Contains(path[:i], ".") {
|
||||||
|
return fmt.Errorf("missing dot in first path element")
|
||||||
|
}
|
||||||
|
if path[0] == '-' {
|
||||||
|
return fmt.Errorf("leading dash in first path element")
|
||||||
|
}
|
||||||
|
for _, r := range path[:i] {
|
||||||
|
if !firstPathOK(r) {
|
||||||
|
return fmt.Errorf("invalid char %q in first path element", r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, _, ok := SplitPathVersion(path); !ok {
|
||||||
|
return fmt.Errorf("invalid version")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckImportPath checks that an import path is valid.
|
||||||
|
//
|
||||||
|
// A valid import path consists of one or more valid path elements
|
||||||
|
// separated by slashes (U+002F). (It must not begin with nor end in a slash.)
|
||||||
|
//
|
||||||
|
// A valid path element is a non-empty string made up of
|
||||||
|
// ASCII letters, ASCII digits, and limited ASCII punctuation: - . _ and ~.
|
||||||
|
// It must not end with a dot (U+002E), nor contain two dots in a row.
|
||||||
|
//
|
||||||
|
// The element prefix up to the first dot must not be a reserved file name
|
||||||
|
// on Windows, regardless of case (CON, com1, NuL, and so on). The element
|
||||||
|
// must not have a suffix of a tilde followed by one or more ASCII digits
|
||||||
|
// (to exclude paths elements that look like Windows short-names).
|
||||||
|
//
|
||||||
|
// CheckImportPath may be less restrictive in the future, but see the
|
||||||
|
// top-level package documentation for additional information about
|
||||||
|
// subtleties of Unicode.
|
||||||
|
func CheckImportPath(path string) error {
|
||||||
|
if err := checkPath(path, importPath); err != nil {
|
||||||
|
return &InvalidPathError{Kind: "import", Path: path, Err: err}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// pathKind indicates what kind of path we're checking. Module paths,
|
||||||
|
// import paths, and file paths have different restrictions.
|
||||||
|
type pathKind int
|
||||||
|
|
||||||
|
const (
|
||||||
|
modulePath pathKind = iota
|
||||||
|
importPath
|
||||||
|
filePath
|
||||||
|
)
|
||||||
|
|
||||||
|
// checkPath checks that a general path is valid. kind indicates what
|
||||||
|
// specific constraints should be applied.
|
||||||
|
//
|
||||||
|
// checkPath returns an error describing why the path is not valid.
|
||||||
|
// Because these checks apply to module, import, and file paths,
|
||||||
|
// and because other checks may be applied, the caller is expected to wrap
|
||||||
|
// this error with InvalidPathError.
|
||||||
|
func checkPath(path string, kind pathKind) error {
|
||||||
|
if !utf8.ValidString(path) {
|
||||||
|
return fmt.Errorf("invalid UTF-8")
|
||||||
|
}
|
||||||
|
if path == "" {
|
||||||
|
return fmt.Errorf("empty string")
|
||||||
|
}
|
||||||
|
if path[0] == '-' && kind != filePath {
|
||||||
|
return fmt.Errorf("leading dash")
|
||||||
|
}
|
||||||
|
if strings.Contains(path, "//") {
|
||||||
|
return fmt.Errorf("double slash")
|
||||||
|
}
|
||||||
|
if path[len(path)-1] == '/' {
|
||||||
|
return fmt.Errorf("trailing slash")
|
||||||
|
}
|
||||||
|
elemStart := 0
|
||||||
|
for i, r := range path {
|
||||||
|
if r == '/' {
|
||||||
|
if err := checkElem(path[elemStart:i], kind); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
elemStart = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := checkElem(path[elemStart:], kind); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkElem checks whether an individual path element is valid.
|
||||||
|
func checkElem(elem string, kind pathKind) error {
|
||||||
|
if elem == "" {
|
||||||
|
return fmt.Errorf("empty path element")
|
||||||
|
}
|
||||||
|
if strings.Count(elem, ".") == len(elem) {
|
||||||
|
return fmt.Errorf("invalid path element %q", elem)
|
||||||
|
}
|
||||||
|
if elem[0] == '.' && kind == modulePath {
|
||||||
|
return fmt.Errorf("leading dot in path element")
|
||||||
|
}
|
||||||
|
if elem[len(elem)-1] == '.' {
|
||||||
|
return fmt.Errorf("trailing dot in path element")
|
||||||
|
}
|
||||||
|
for _, r := range elem {
|
||||||
|
ok := false
|
||||||
|
switch kind {
|
||||||
|
case modulePath:
|
||||||
|
ok = modPathOK(r)
|
||||||
|
case importPath:
|
||||||
|
ok = importPathOK(r)
|
||||||
|
case filePath:
|
||||||
|
ok = fileNameOK(r)
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("internal error: invalid kind %v", kind))
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid char %q", r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Windows disallows a bunch of path elements, sadly.
|
||||||
|
// See https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file
|
||||||
|
short := elem
|
||||||
|
if i := strings.Index(short, "."); i >= 0 {
|
||||||
|
short = short[:i]
|
||||||
|
}
|
||||||
|
for _, bad := range badWindowsNames {
|
||||||
|
if strings.EqualFold(bad, short) {
|
||||||
|
return fmt.Errorf("%q disallowed as path element component on Windows", short)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if kind == filePath {
|
||||||
|
// don't check for Windows short-names in file names. They're
|
||||||
|
// only an issue for import paths.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reject path components that look like Windows short-names.
|
||||||
|
// Those usually end in a tilde followed by one or more ASCII digits.
|
||||||
|
if tilde := strings.LastIndexByte(short, '~'); tilde >= 0 && tilde < len(short)-1 {
|
||||||
|
suffix := short[tilde+1:]
|
||||||
|
suffixIsDigits := true
|
||||||
|
for _, r := range suffix {
|
||||||
|
if r < '0' || r > '9' {
|
||||||
|
suffixIsDigits = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if suffixIsDigits {
|
||||||
|
return fmt.Errorf("trailing tilde and digits in path element")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckFilePath checks that a slash-separated file path is valid.
|
||||||
|
// The definition of a valid file path is the same as the definition
|
||||||
|
// of a valid import path except that the set of allowed characters is larger:
|
||||||
|
// all Unicode letters, ASCII digits, the ASCII space character (U+0020),
|
||||||
|
// and the ASCII punctuation characters
|
||||||
|
// “!#$%&()+,-.=@[]^_{}~”.
|
||||||
|
// (The excluded punctuation characters, " * < > ? ` ' | / \ and :,
|
||||||
|
// have special meanings in certain shells or operating systems.)
|
||||||
|
//
|
||||||
|
// CheckFilePath may be less restrictive in the future, but see the
|
||||||
|
// top-level package documentation for additional information about
|
||||||
|
// subtleties of Unicode.
|
||||||
|
func CheckFilePath(path string) error {
|
||||||
|
if err := checkPath(path, filePath); err != nil {
|
||||||
|
return &InvalidPathError{Kind: "file", Path: path, Err: err}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// badWindowsNames are the reserved file path elements on Windows.
|
||||||
|
// See https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file
|
||||||
|
var badWindowsNames = []string{
|
||||||
|
"CON",
|
||||||
|
"PRN",
|
||||||
|
"AUX",
|
||||||
|
"NUL",
|
||||||
|
"COM1",
|
||||||
|
"COM2",
|
||||||
|
"COM3",
|
||||||
|
"COM4",
|
||||||
|
"COM5",
|
||||||
|
"COM6",
|
||||||
|
"COM7",
|
||||||
|
"COM8",
|
||||||
|
"COM9",
|
||||||
|
"LPT1",
|
||||||
|
"LPT2",
|
||||||
|
"LPT3",
|
||||||
|
"LPT4",
|
||||||
|
"LPT5",
|
||||||
|
"LPT6",
|
||||||
|
"LPT7",
|
||||||
|
"LPT8",
|
||||||
|
"LPT9",
|
||||||
|
}
|
||||||
|
|
||||||
|
// SplitPathVersion returns prefix and major version such that prefix+pathMajor == path
|
||||||
|
// and version is either empty or "/vN" for N >= 2.
|
||||||
|
// As a special case, gopkg.in paths are recognized directly;
|
||||||
|
// they require ".vN" instead of "/vN", and for all N, not just N >= 2.
|
||||||
|
// SplitPathVersion returns with ok = false when presented with
|
||||||
|
// a path whose last path element does not satisfy the constraints
|
||||||
|
// applied by CheckPath, such as "example.com/pkg/v1" or "example.com/pkg/v1.2".
|
||||||
|
func SplitPathVersion(path string) (prefix, pathMajor string, ok bool) {
|
||||||
|
if strings.HasPrefix(path, "gopkg.in/") {
|
||||||
|
return splitGopkgIn(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
i := len(path)
|
||||||
|
dot := false
|
||||||
|
for i > 0 && ('0' <= path[i-1] && path[i-1] <= '9' || path[i-1] == '.') {
|
||||||
|
if path[i-1] == '.' {
|
||||||
|
dot = true
|
||||||
|
}
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
if i <= 1 || i == len(path) || path[i-1] != 'v' || path[i-2] != '/' {
|
||||||
|
return path, "", true
|
||||||
|
}
|
||||||
|
prefix, pathMajor = path[:i-2], path[i-2:]
|
||||||
|
if dot || len(pathMajor) <= 2 || pathMajor[2] == '0' || pathMajor == "/v1" {
|
||||||
|
return path, "", false
|
||||||
|
}
|
||||||
|
return prefix, pathMajor, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// splitGopkgIn is like SplitPathVersion but only for gopkg.in paths.
|
||||||
|
func splitGopkgIn(path string) (prefix, pathMajor string, ok bool) {
|
||||||
|
if !strings.HasPrefix(path, "gopkg.in/") {
|
||||||
|
return path, "", false
|
||||||
|
}
|
||||||
|
i := len(path)
|
||||||
|
if strings.HasSuffix(path, "-unstable") {
|
||||||
|
i -= len("-unstable")
|
||||||
|
}
|
||||||
|
for i > 0 && ('0' <= path[i-1] && path[i-1] <= '9') {
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
if i <= 1 || path[i-1] != 'v' || path[i-2] != '.' {
|
||||||
|
// All gopkg.in paths must end in vN for some N.
|
||||||
|
return path, "", false
|
||||||
|
}
|
||||||
|
prefix, pathMajor = path[:i-2], path[i-2:]
|
||||||
|
if len(pathMajor) <= 2 || pathMajor[2] == '0' && pathMajor != ".v0" {
|
||||||
|
return path, "", false
|
||||||
|
}
|
||||||
|
return prefix, pathMajor, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchPathMajor reports whether the semantic version v
|
||||||
|
// matches the path major version pathMajor.
|
||||||
|
//
|
||||||
|
// MatchPathMajor returns true if and only if CheckPathMajor returns nil.
|
||||||
|
func MatchPathMajor(v, pathMajor string) bool {
|
||||||
|
return CheckPathMajor(v, pathMajor) == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckPathMajor returns a non-nil error if the semantic version v
|
||||||
|
// does not match the path major version pathMajor.
|
||||||
|
func CheckPathMajor(v, pathMajor string) error {
|
||||||
|
// TODO(jayconrod): return errors or panic for invalid inputs. This function
|
||||||
|
// (and others) was covered by integration tests for cmd/go, and surrounding
|
||||||
|
// code protected against invalid inputs like non-canonical versions.
|
||||||
|
if strings.HasPrefix(pathMajor, ".v") && strings.HasSuffix(pathMajor, "-unstable") {
|
||||||
|
pathMajor = strings.TrimSuffix(pathMajor, "-unstable")
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(v, "v0.0.0-") && pathMajor == ".v1" {
|
||||||
|
// Allow old bug in pseudo-versions that generated v0.0.0- pseudoversion for gopkg .v1.
|
||||||
|
// For example, gopkg.in/yaml.v2@v2.2.1's go.mod requires gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
m := semver.Major(v)
|
||||||
|
if pathMajor == "" {
|
||||||
|
if m == "v0" || m == "v1" || semver.Build(v) == "+incompatible" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
pathMajor = "v0 or v1"
|
||||||
|
} else if pathMajor[0] == '/' || pathMajor[0] == '.' {
|
||||||
|
if m == pathMajor[1:] {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
pathMajor = pathMajor[1:]
|
||||||
|
}
|
||||||
|
return &InvalidVersionError{
|
||||||
|
Version: v,
|
||||||
|
Err: fmt.Errorf("should be %s, not %s", pathMajor, semver.Major(v)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PathMajorPrefix returns the major-version tag prefix implied by pathMajor.
|
||||||
|
// An empty PathMajorPrefix allows either v0 or v1.
|
||||||
|
//
|
||||||
|
// Note that MatchPathMajor may accept some versions that do not actually begin
|
||||||
|
// with this prefix: namely, it accepts a 'v0.0.0-' prefix for a '.v1'
|
||||||
|
// pathMajor, even though that pathMajor implies 'v1' tagging.
|
||||||
|
func PathMajorPrefix(pathMajor string) string {
|
||||||
|
if pathMajor == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if pathMajor[0] != '/' && pathMajor[0] != '.' {
|
||||||
|
panic("pathMajor suffix " + pathMajor + " passed to PathMajorPrefix lacks separator")
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(pathMajor, ".v") && strings.HasSuffix(pathMajor, "-unstable") {
|
||||||
|
pathMajor = strings.TrimSuffix(pathMajor, "-unstable")
|
||||||
|
}
|
||||||
|
m := pathMajor[1:]
|
||||||
|
if m != semver.Major(m) {
|
||||||
|
panic("pathMajor suffix " + pathMajor + "passed to PathMajorPrefix is not a valid major version")
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanonicalVersion returns the canonical form of the version string v.
|
||||||
|
// It is the same as semver.Canonical(v) except that it preserves the special build suffix "+incompatible".
|
||||||
|
func CanonicalVersion(v string) string {
|
||||||
|
cv := semver.Canonical(v)
|
||||||
|
if semver.Build(v) == "+incompatible" {
|
||||||
|
cv += "+incompatible"
|
||||||
|
}
|
||||||
|
return cv
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort sorts the list by Path, breaking ties by comparing Version fields.
|
||||||
|
// The Version fields are interpreted as semantic versions (using semver.Compare)
|
||||||
|
// optionally followed by a tie-breaking suffix introduced by a slash character,
|
||||||
|
// like in "v0.0.1/go.mod".
|
||||||
|
func Sort(list []Version) {
|
||||||
|
sort.Slice(list, func(i, j int) bool {
|
||||||
|
mi := list[i]
|
||||||
|
mj := list[j]
|
||||||
|
if mi.Path != mj.Path {
|
||||||
|
return mi.Path < mj.Path
|
||||||
|
}
|
||||||
|
// To help go.sum formatting, allow version/file.
|
||||||
|
// Compare semver prefix by semver rules,
|
||||||
|
// file by string order.
|
||||||
|
vi := mi.Version
|
||||||
|
vj := mj.Version
|
||||||
|
var fi, fj string
|
||||||
|
if k := strings.Index(vi, "/"); k >= 0 {
|
||||||
|
vi, fi = vi[:k], vi[k:]
|
||||||
|
}
|
||||||
|
if k := strings.Index(vj, "/"); k >= 0 {
|
||||||
|
vj, fj = vj[:k], vj[k:]
|
||||||
|
}
|
||||||
|
if vi != vj {
|
||||||
|
return semver.Compare(vi, vj) < 0
|
||||||
|
}
|
||||||
|
return fi < fj
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// EscapePath returns the escaped form of the given module path.
|
||||||
|
// It fails if the module path is invalid.
|
||||||
|
func EscapePath(path string) (escaped string, err error) {
|
||||||
|
if err := CheckPath(path); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return escapeString(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EscapeVersion returns the escaped form of the given module version.
|
||||||
|
// Versions are allowed to be in non-semver form but must be valid file names
|
||||||
|
// and not contain exclamation marks.
|
||||||
|
func EscapeVersion(v string) (escaped string, err error) {
|
||||||
|
if err := checkElem(v, filePath); err != nil || strings.Contains(v, "!") {
|
||||||
|
return "", &InvalidVersionError{
|
||||||
|
Version: v,
|
||||||
|
Err: fmt.Errorf("disallowed version string"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return escapeString(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func escapeString(s string) (escaped string, err error) {
|
||||||
|
haveUpper := false
|
||||||
|
for _, r := range s {
|
||||||
|
if r == '!' || r >= utf8.RuneSelf {
|
||||||
|
// This should be disallowed by CheckPath, but diagnose anyway.
|
||||||
|
// The correctness of the escaping loop below depends on it.
|
||||||
|
return "", fmt.Errorf("internal error: inconsistency in EscapePath")
|
||||||
|
}
|
||||||
|
if 'A' <= r && r <= 'Z' {
|
||||||
|
haveUpper = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !haveUpper {
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf []byte
|
||||||
|
for _, r := range s {
|
||||||
|
if 'A' <= r && r <= 'Z' {
|
||||||
|
buf = append(buf, '!', byte(r+'a'-'A'))
|
||||||
|
} else {
|
||||||
|
buf = append(buf, byte(r))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnescapePath returns the module path for the given escaped path.
|
||||||
|
// It fails if the escaped path is invalid or describes an invalid path.
|
||||||
|
func UnescapePath(escaped string) (path string, err error) {
|
||||||
|
path, ok := unescapeString(escaped)
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("invalid escaped module path %q", escaped)
|
||||||
|
}
|
||||||
|
if err := CheckPath(path); err != nil {
|
||||||
|
return "", fmt.Errorf("invalid escaped module path %q: %v", escaped, err)
|
||||||
|
}
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnescapeVersion returns the version string for the given escaped version.
|
||||||
|
// It fails if the escaped form is invalid or describes an invalid version.
|
||||||
|
// Versions are allowed to be in non-semver form but must be valid file names
|
||||||
|
// and not contain exclamation marks.
|
||||||
|
func UnescapeVersion(escaped string) (v string, err error) {
|
||||||
|
v, ok := unescapeString(escaped)
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("invalid escaped version %q", escaped)
|
||||||
|
}
|
||||||
|
if err := checkElem(v, filePath); err != nil {
|
||||||
|
return "", fmt.Errorf("invalid escaped version %q: %v", v, err)
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unescapeString(escaped string) (string, bool) {
|
||||||
|
var buf []byte
|
||||||
|
|
||||||
|
bang := false
|
||||||
|
for _, r := range escaped {
|
||||||
|
if r >= utf8.RuneSelf {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
if bang {
|
||||||
|
bang = false
|
||||||
|
if r < 'a' || 'z' < r {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
buf = append(buf, byte(r+'A'-'a'))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if r == '!' {
|
||||||
|
bang = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if 'A' <= r && r <= 'Z' {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
buf = append(buf, byte(r))
|
||||||
|
}
|
||||||
|
if bang {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
return string(buf), true
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchPrefixPatterns reports whether any path prefix of target matches one of
|
||||||
|
// the glob patterns (as defined by path.Match) in the comma-separated globs
|
||||||
|
// list. This implements the algorithm used when matching a module path to the
|
||||||
|
// GOPRIVATE environment variable, as described by 'go help module-private'.
|
||||||
|
//
|
||||||
|
// It ignores any empty or malformed patterns in the list.
|
||||||
|
func MatchPrefixPatterns(globs, target string) bool {
|
||||||
|
for globs != "" {
|
||||||
|
// Extract next non-empty glob in comma-separated list.
|
||||||
|
var glob string
|
||||||
|
if i := strings.Index(globs, ","); i >= 0 {
|
||||||
|
glob, globs = globs[:i], globs[i+1:]
|
||||||
|
} else {
|
||||||
|
glob, globs = globs, ""
|
||||||
|
}
|
||||||
|
if glob == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// A glob with N+1 path elements (N slashes) needs to be matched
|
||||||
|
// against the first N+1 path elements of target,
|
||||||
|
// which end just before the N+1'th slash.
|
||||||
|
n := strings.Count(glob, "/")
|
||||||
|
prefix := target
|
||||||
|
// Walk target, counting slashes, truncating at the N+1'th slash.
|
||||||
|
for i := 0; i < len(target); i++ {
|
||||||
|
if target[i] == '/' {
|
||||||
|
if n == 0 {
|
||||||
|
prefix = target[:i]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
n--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if n > 0 {
|
||||||
|
// Not enough prefix elements.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
matched, _ := path.Match(glob, prefix)
|
||||||
|
if matched {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
250
vendor/golang.org/x/mod/module/pseudo.go
generated
vendored
Normal file
250
vendor/golang.org/x/mod/module/pseudo.go
generated
vendored
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
// Copyright 2018 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Pseudo-versions
|
||||||
|
//
|
||||||
|
// Code authors are expected to tag the revisions they want users to use,
|
||||||
|
// including prereleases. However, not all authors tag versions at all,
|
||||||
|
// and not all commits a user might want to try will have tags.
|
||||||
|
// A pseudo-version is a version with a special form that allows us to
|
||||||
|
// address an untagged commit and order that version with respect to
|
||||||
|
// other versions we might encounter.
|
||||||
|
//
|
||||||
|
// A pseudo-version takes one of the general forms:
|
||||||
|
//
|
||||||
|
// (1) vX.0.0-yyyymmddhhmmss-abcdef123456
|
||||||
|
// (2) vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdef123456
|
||||||
|
// (3) vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdef123456+incompatible
|
||||||
|
// (4) vX.Y.Z-pre.0.yyyymmddhhmmss-abcdef123456
|
||||||
|
// (5) vX.Y.Z-pre.0.yyyymmddhhmmss-abcdef123456+incompatible
|
||||||
|
//
|
||||||
|
// If there is no recently tagged version with the right major version vX,
|
||||||
|
// then form (1) is used, creating a space of pseudo-versions at the bottom
|
||||||
|
// of the vX version range, less than any tagged version, including the unlikely v0.0.0.
|
||||||
|
//
|
||||||
|
// If the most recent tagged version before the target commit is vX.Y.Z or vX.Y.Z+incompatible,
|
||||||
|
// then the pseudo-version uses form (2) or (3), making it a prerelease for the next
|
||||||
|
// possible semantic version after vX.Y.Z. The leading 0 segment in the prerelease string
|
||||||
|
// ensures that the pseudo-version compares less than possible future explicit prereleases
|
||||||
|
// like vX.Y.(Z+1)-rc1 or vX.Y.(Z+1)-1.
|
||||||
|
//
|
||||||
|
// If the most recent tagged version before the target commit is vX.Y.Z-pre or vX.Y.Z-pre+incompatible,
|
||||||
|
// then the pseudo-version uses form (4) or (5), making it a slightly later prerelease.
|
||||||
|
|
||||||
|
package module
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/mod/internal/lazyregexp"
|
||||||
|
"golang.org/x/mod/semver"
|
||||||
|
)
|
||||||
|
|
||||||
|
var pseudoVersionRE = lazyregexp.New(`^v[0-9]+\.(0\.0-|\d+\.\d+-([^+]*\.)?0\.)\d{14}-[A-Za-z0-9]+(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$`)
|
||||||
|
|
||||||
|
const PseudoVersionTimestampFormat = "20060102150405"
|
||||||
|
|
||||||
|
// PseudoVersion returns a pseudo-version for the given major version ("v1")
|
||||||
|
// preexisting older tagged version ("" or "v1.2.3" or "v1.2.3-pre"), revision time,
|
||||||
|
// and revision identifier (usually a 12-byte commit hash prefix).
|
||||||
|
func PseudoVersion(major, older string, t time.Time, rev string) string {
|
||||||
|
if major == "" {
|
||||||
|
major = "v0"
|
||||||
|
}
|
||||||
|
segment := fmt.Sprintf("%s-%s", t.UTC().Format(PseudoVersionTimestampFormat), rev)
|
||||||
|
build := semver.Build(older)
|
||||||
|
older = semver.Canonical(older)
|
||||||
|
if older == "" {
|
||||||
|
return major + ".0.0-" + segment // form (1)
|
||||||
|
}
|
||||||
|
if semver.Prerelease(older) != "" {
|
||||||
|
return older + ".0." + segment + build // form (4), (5)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Form (2), (3).
|
||||||
|
// Extract patch from vMAJOR.MINOR.PATCH
|
||||||
|
i := strings.LastIndex(older, ".") + 1
|
||||||
|
v, patch := older[:i], older[i:]
|
||||||
|
|
||||||
|
// Reassemble.
|
||||||
|
return v + incDecimal(patch) + "-0." + segment + build
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZeroPseudoVersion returns a pseudo-version with a zero timestamp and
|
||||||
|
// revision, which may be used as a placeholder.
|
||||||
|
func ZeroPseudoVersion(major string) string {
|
||||||
|
return PseudoVersion(major, "", time.Time{}, "000000000000")
|
||||||
|
}
|
||||||
|
|
||||||
|
// incDecimal returns the decimal string incremented by 1.
|
||||||
|
func incDecimal(decimal string) string {
|
||||||
|
// Scan right to left turning 9s to 0s until you find a digit to increment.
|
||||||
|
digits := []byte(decimal)
|
||||||
|
i := len(digits) - 1
|
||||||
|
for ; i >= 0 && digits[i] == '9'; i-- {
|
||||||
|
digits[i] = '0'
|
||||||
|
}
|
||||||
|
if i >= 0 {
|
||||||
|
digits[i]++
|
||||||
|
} else {
|
||||||
|
// digits is all zeros
|
||||||
|
digits[0] = '1'
|
||||||
|
digits = append(digits, '0')
|
||||||
|
}
|
||||||
|
return string(digits)
|
||||||
|
}
|
||||||
|
|
||||||
|
// decDecimal returns the decimal string decremented by 1, or the empty string
|
||||||
|
// if the decimal is all zeroes.
|
||||||
|
func decDecimal(decimal string) string {
|
||||||
|
// Scan right to left turning 0s to 9s until you find a digit to decrement.
|
||||||
|
digits := []byte(decimal)
|
||||||
|
i := len(digits) - 1
|
||||||
|
for ; i >= 0 && digits[i] == '0'; i-- {
|
||||||
|
digits[i] = '9'
|
||||||
|
}
|
||||||
|
if i < 0 {
|
||||||
|
// decimal is all zeros
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if i == 0 && digits[i] == '1' && len(digits) > 1 {
|
||||||
|
digits = digits[1:]
|
||||||
|
} else {
|
||||||
|
digits[i]--
|
||||||
|
}
|
||||||
|
return string(digits)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPseudoVersion reports whether v is a pseudo-version.
|
||||||
|
func IsPseudoVersion(v string) bool {
|
||||||
|
return strings.Count(v, "-") >= 2 && semver.IsValid(v) && pseudoVersionRE.MatchString(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsZeroPseudoVersion returns whether v is a pseudo-version with a zero base,
|
||||||
|
// timestamp, and revision, as returned by ZeroPseudoVersion.
|
||||||
|
func IsZeroPseudoVersion(v string) bool {
|
||||||
|
return v == ZeroPseudoVersion(semver.Major(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// PseudoVersionTime returns the time stamp of the pseudo-version v.
|
||||||
|
// It returns an error if v is not a pseudo-version or if the time stamp
|
||||||
|
// embedded in the pseudo-version is not a valid time.
|
||||||
|
func PseudoVersionTime(v string) (time.Time, error) {
|
||||||
|
_, timestamp, _, _, err := parsePseudoVersion(v)
|
||||||
|
if err != nil {
|
||||||
|
return time.Time{}, err
|
||||||
|
}
|
||||||
|
t, err := time.Parse("20060102150405", timestamp)
|
||||||
|
if err != nil {
|
||||||
|
return time.Time{}, &InvalidVersionError{
|
||||||
|
Version: v,
|
||||||
|
Pseudo: true,
|
||||||
|
Err: fmt.Errorf("malformed time %q", timestamp),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PseudoVersionRev returns the revision identifier of the pseudo-version v.
|
||||||
|
// It returns an error if v is not a pseudo-version.
|
||||||
|
func PseudoVersionRev(v string) (rev string, err error) {
|
||||||
|
_, _, rev, _, err = parsePseudoVersion(v)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// PseudoVersionBase returns the canonical parent version, if any, upon which
|
||||||
|
// the pseudo-version v is based.
|
||||||
|
//
|
||||||
|
// If v has no parent version (that is, if it is "vX.0.0-[…]"),
|
||||||
|
// PseudoVersionBase returns the empty string and a nil error.
|
||||||
|
func PseudoVersionBase(v string) (string, error) {
|
||||||
|
base, _, _, build, err := parsePseudoVersion(v)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch pre := semver.Prerelease(base); pre {
|
||||||
|
case "":
|
||||||
|
// vX.0.0-yyyymmddhhmmss-abcdef123456 → ""
|
||||||
|
if build != "" {
|
||||||
|
// Pseudo-versions of the form vX.0.0-yyyymmddhhmmss-abcdef123456+incompatible
|
||||||
|
// are nonsensical: the "vX.0.0-" prefix implies that there is no parent tag,
|
||||||
|
// but the "+incompatible" suffix implies that the major version of
|
||||||
|
// the parent tag is not compatible with the module's import path.
|
||||||
|
//
|
||||||
|
// There are a few such entries in the index generated by proxy.golang.org,
|
||||||
|
// but we believe those entries were generated by the proxy itself.
|
||||||
|
return "", &InvalidVersionError{
|
||||||
|
Version: v,
|
||||||
|
Pseudo: true,
|
||||||
|
Err: fmt.Errorf("lacks base version, but has build metadata %q", build),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", nil
|
||||||
|
|
||||||
|
case "-0":
|
||||||
|
// vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdef123456 → vX.Y.Z
|
||||||
|
// vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdef123456+incompatible → vX.Y.Z+incompatible
|
||||||
|
base = strings.TrimSuffix(base, pre)
|
||||||
|
i := strings.LastIndexByte(base, '.')
|
||||||
|
if i < 0 {
|
||||||
|
panic("base from parsePseudoVersion missing patch number: " + base)
|
||||||
|
}
|
||||||
|
patch := decDecimal(base[i+1:])
|
||||||
|
if patch == "" {
|
||||||
|
// vX.0.0-0 is invalid, but has been observed in the wild in the index
|
||||||
|
// generated by requests to proxy.golang.org.
|
||||||
|
//
|
||||||
|
// NOTE(bcmills): I cannot find a historical bug that accounts for
|
||||||
|
// pseudo-versions of this form, nor have I seen such versions in any
|
||||||
|
// actual go.mod files. If we find actual examples of this form and a
|
||||||
|
// reasonable theory of how they came into existence, it seems fine to
|
||||||
|
// treat them as equivalent to vX.0.0 (especially since the invalid
|
||||||
|
// pseudo-versions have lower precedence than the real ones). For now, we
|
||||||
|
// reject them.
|
||||||
|
return "", &InvalidVersionError{
|
||||||
|
Version: v,
|
||||||
|
Pseudo: true,
|
||||||
|
Err: fmt.Errorf("version before %s would have negative patch number", base),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return base[:i+1] + patch + build, nil
|
||||||
|
|
||||||
|
default:
|
||||||
|
// vX.Y.Z-pre.0.yyyymmddhhmmss-abcdef123456 → vX.Y.Z-pre
|
||||||
|
// vX.Y.Z-pre.0.yyyymmddhhmmss-abcdef123456+incompatible → vX.Y.Z-pre+incompatible
|
||||||
|
if !strings.HasSuffix(base, ".0") {
|
||||||
|
panic(`base from parsePseudoVersion missing ".0" before date: ` + base)
|
||||||
|
}
|
||||||
|
return strings.TrimSuffix(base, ".0") + build, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var errPseudoSyntax = errors.New("syntax error")
|
||||||
|
|
||||||
|
func parsePseudoVersion(v string) (base, timestamp, rev, build string, err error) {
|
||||||
|
if !IsPseudoVersion(v) {
|
||||||
|
return "", "", "", "", &InvalidVersionError{
|
||||||
|
Version: v,
|
||||||
|
Pseudo: true,
|
||||||
|
Err: errPseudoSyntax,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
build = semver.Build(v)
|
||||||
|
v = strings.TrimSuffix(v, build)
|
||||||
|
j := strings.LastIndex(v, "-")
|
||||||
|
v, rev = v[:j], v[j+1:]
|
||||||
|
i := strings.LastIndex(v, "-")
|
||||||
|
if j := strings.LastIndex(v, "."); j > i {
|
||||||
|
base = v[:j] // "vX.Y.Z-pre.0" or "vX.Y.(Z+1)-0"
|
||||||
|
timestamp = v[j+1:]
|
||||||
|
} else {
|
||||||
|
base = v[:i] // "vX.0.0"
|
||||||
|
timestamp = v[i+1:]
|
||||||
|
}
|
||||||
|
return base, timestamp, rev, build, nil
|
||||||
|
}
|
||||||
20
vendor/golang.org/x/mod/semver/semver.go
generated
vendored
20
vendor/golang.org/x/mod/semver/semver.go
generated
vendored
@@ -22,6 +22,8 @@
|
|||||||
// as shorthands for vMAJOR.0.0 and vMAJOR.MINOR.0.
|
// as shorthands for vMAJOR.0.0 and vMAJOR.MINOR.0.
|
||||||
package semver
|
package semver
|
||||||
|
|
||||||
|
import "sort"
|
||||||
|
|
||||||
// parsed returns the parsed form of a semantic version string.
|
// parsed returns the parsed form of a semantic version string.
|
||||||
type parsed struct {
|
type parsed struct {
|
||||||
major string
|
major string
|
||||||
@@ -150,6 +152,24 @@ func Max(v, w string) string {
|
|||||||
return w
|
return w
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ByVersion implements sort.Interface for sorting semantic version strings.
|
||||||
|
type ByVersion []string
|
||||||
|
|
||||||
|
func (vs ByVersion) Len() int { return len(vs) }
|
||||||
|
func (vs ByVersion) Swap(i, j int) { vs[i], vs[j] = vs[j], vs[i] }
|
||||||
|
func (vs ByVersion) Less(i, j int) bool {
|
||||||
|
cmp := Compare(vs[i], vs[j])
|
||||||
|
if cmp != 0 {
|
||||||
|
return cmp < 0
|
||||||
|
}
|
||||||
|
return vs[i] < vs[j]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort sorts a list of semantic version strings using ByVersion.
|
||||||
|
func Sort(list []string) {
|
||||||
|
sort.Sort(ByVersion(list))
|
||||||
|
}
|
||||||
|
|
||||||
func parse(v string) (p parsed, ok bool) {
|
func parse(v string) (p parsed, ok bool) {
|
||||||
if v == "" || v[0] != 'v' {
|
if v == "" || v[0] != 'v' {
|
||||||
p.err = "missing v prefix"
|
p.err = "missing v prefix"
|
||||||
|
|||||||
18
vendor/modules.txt
vendored
18
vendor/modules.txt
vendored
@@ -515,6 +515,19 @@ github.com/prometheus/common/model
|
|||||||
github.com/prometheus/procfs
|
github.com/prometheus/procfs
|
||||||
github.com/prometheus/procfs/internal/fs
|
github.com/prometheus/procfs/internal/fs
|
||||||
github.com/prometheus/procfs/internal/util
|
github.com/prometheus/procfs/internal/util
|
||||||
|
# github.com/redhat-developer/alizer/go v0.0.0-20220215154256-33df7feef4ae
|
||||||
|
## explicit
|
||||||
|
github.com/redhat-developer/alizer/go/pkg/apis/enricher
|
||||||
|
github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/dotnet
|
||||||
|
github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/go
|
||||||
|
github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/java
|
||||||
|
github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/javascript/nodejs
|
||||||
|
github.com/redhat-developer/alizer/go/pkg/apis/enricher/framework/python
|
||||||
|
github.com/redhat-developer/alizer/go/pkg/apis/language
|
||||||
|
github.com/redhat-developer/alizer/go/pkg/apis/recognizer
|
||||||
|
github.com/redhat-developer/alizer/go/pkg/schema
|
||||||
|
github.com/redhat-developer/alizer/go/pkg/utils
|
||||||
|
github.com/redhat-developer/alizer/go/pkg/utils/langfiles
|
||||||
# github.com/redhat-developer/service-binding-operator v0.9.0
|
# github.com/redhat-developer/service-binding-operator v0.9.0
|
||||||
## explicit
|
## explicit
|
||||||
github.com/redhat-developer/service-binding-operator/apis
|
github.com/redhat-developer/service-binding-operator/apis
|
||||||
@@ -633,7 +646,10 @@ golang.org/x/crypto/ssh
|
|||||||
golang.org/x/crypto/ssh/agent
|
golang.org/x/crypto/ssh/agent
|
||||||
golang.org/x/crypto/ssh/internal/bcrypt_pbkdf
|
golang.org/x/crypto/ssh/internal/bcrypt_pbkdf
|
||||||
golang.org/x/crypto/ssh/knownhosts
|
golang.org/x/crypto/ssh/knownhosts
|
||||||
# golang.org/x/mod v0.4.2
|
# golang.org/x/mod v0.5.1
|
||||||
|
golang.org/x/mod/internal/lazyregexp
|
||||||
|
golang.org/x/mod/modfile
|
||||||
|
golang.org/x/mod/module
|
||||||
golang.org/x/mod/semver
|
golang.org/x/mod/semver
|
||||||
# golang.org/x/net v0.0.0-20210520170846-37e1c6afe023
|
# golang.org/x/net v0.0.0-20210520170846-37e1c6afe023
|
||||||
golang.org/x/net/context
|
golang.org/x/net/context
|
||||||
|
|||||||
Reference in New Issue
Block a user