Skip to content
Merged
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
## About

The Devfile Parser library is a Golang module that:
1. parses the devfile.yaml as specified by the [api](https://devfile.github.io/devfile/api-reference.html) & [schema](https://github.com/devfile/api/tree/main/schemas/latest).
2. writes to the devfile.yaml with the updated data.
1. parses a devfile as specified by the [api](https://devfile.io/docs/2.2.1/devfile-schema) & [schema](https://github.com/devfile/api/tree/main/schemas/latest).
2. writes to the specified devfile with the updated data.
3. generates Kubernetes objects for the various devfile resources.
4. defines util functions for the devfile.
5. downloads resources from a parent devfile if specified in the devfile.yaml
5. downloads resources from a parent devfile if specified in the devfile.

## Private repository support

Expand Down
235 changes: 234 additions & 1 deletion pkg/devfile/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package devfile

import (
"context"
"fmt"
"net"
"net/http"
"net/http/httptest"
Expand Down Expand Up @@ -273,6 +274,7 @@ spec:
wantKubernetesInline string
wantOpenshiftInline string
wantVariables map[string]string
additionalChecks func(parser.DevfileObj) error
}{
{
name: "with external overriding variables",
Expand Down Expand Up @@ -415,7 +417,7 @@ spec:
ExternalVariables: map[string]string{
"PARAMS": "baz",
},
Path: "./testdata/devfile1.yaml",
Path: "./testdata/devfile.yaml",
},
},
wantCommandLine: "./main baz",
Expand Down Expand Up @@ -527,6 +529,230 @@ spec:
StarterProjects: map[string][]string{},
},
},
{
name: "parsing devfile with context path containing multiple devfiles => priority to devfile.yaml",
args: args{
args: parser.ParserArgs{
ExternalVariables: map[string]string{
"PARAMS": "from devfile.yaml based on priority",
},
Path: "./testdata",
},
},
wantCommandLine: "./main from devfile.yaml based on priority",
wantVariables: map[string]string{
"PARAMS": "from devfile.yaml based on priority",
},
wantVarWarning: variables.VariableWarning{
Commands: map[string][]string{},
Components: map[string][]string{},
Projects: map[string][]string{},
StarterProjects: map[string][]string{},
},
additionalChecks: func(devfileObj parser.DevfileObj) error {
if devfileObj.Data.GetMetadata().DisplayName != "Go Runtime (devfile.yaml)" {
return fmt.Errorf("expected 'Go Runtime (devfile.yaml)' as metadata.displayName in devfile, but got %q",
devfileObj.Data.GetMetadata().DisplayName)
}
return nil
},
},
{
name: "parsing devfile with context path containing multiple devfiles => priority to .devfile.yaml",
args: args{
args: parser.ParserArgs{
ExternalVariables: map[string]string{
"PARAMS": "from .devfile.yaml based on priority",
},
Path: "./testdata/priority-for-dot_devfile_yaml",
},
},
wantCommandLine: "./main from .devfile.yaml based on priority",
wantVariables: map[string]string{
"PARAMS": "from .devfile.yaml based on priority",
},
wantVarWarning: variables.VariableWarning{
Commands: map[string][]string{},
Components: map[string][]string{},
Projects: map[string][]string{},
StarterProjects: map[string][]string{},
},
additionalChecks: func(devfileObj parser.DevfileObj) error {
if devfileObj.Data.GetMetadata().DisplayName != "Go Runtime (.devfile.yaml)" {
return fmt.Errorf("expected 'Go Runtime (.devfile.yaml)' as metadata.displayName in devfile, but got %q",
devfileObj.Data.GetMetadata().DisplayName)
}
return nil
},
},
{
name: "parsing devfile with context path containing multiple devfiles => priority to devfile.yml",
args: args{
args: parser.ParserArgs{
ExternalVariables: map[string]string{
"PARAMS": "from devfile.yml based on priority",
},
Path: "./testdata/priority-for-devfile_yml",
},
},
wantCommandLine: "./main from devfile.yml based on priority",
wantVariables: map[string]string{
"PARAMS": "from devfile.yml based on priority",
},
wantVarWarning: variables.VariableWarning{
Commands: map[string][]string{},
Components: map[string][]string{},
Projects: map[string][]string{},
StarterProjects: map[string][]string{},
},
additionalChecks: func(devfileObj parser.DevfileObj) error {
if devfileObj.Data.GetMetadata().DisplayName != "Test stack (devfile.yml)" {
return fmt.Errorf("expected 'Test stack (devfile.yml)' as metadata.displayName in devfile, but got %q",
devfileObj.Data.GetMetadata().DisplayName)
}
return nil
},
},
{
name: "parsing devfile with context path containing multiple devfiles => priority to .devfile.yml",
args: args{
args: parser.ParserArgs{
ExternalVariables: map[string]string{
"PARAMS": "from .devfile.yml based on priority",
},
Path: "./testdata/priority-for-dot_devfile_yml",
},
},
wantCommandLine: "./main from .devfile.yml based on priority",
wantVariables: map[string]string{
"PARAMS": "from .devfile.yml based on priority",
},
wantVarWarning: variables.VariableWarning{
Commands: map[string][]string{},
Components: map[string][]string{},
Projects: map[string][]string{},
StarterProjects: map[string][]string{},
},
additionalChecks: func(devfileObj parser.DevfileObj) error {
if devfileObj.Data.GetMetadata().DisplayName != "Test stack (.devfile.yml)" {
return fmt.Errorf("expected 'Test stack (.devfile.yml)' as metadata.displayName in devfile, but got %q",
devfileObj.Data.GetMetadata().DisplayName)
}
return nil
},
},
{
name: "parsing devfile with .yml extension",
args: args{
args: parser.ParserArgs{
ExternalVariables: map[string]string{
"PARAMS": "from devfile.yml",
},
Path: "./testdata/devfile.yml",
},
},
wantCommandLine: "./main from devfile.yml",
wantVariables: map[string]string{
"PARAMS": "from devfile.yml",
},
wantVarWarning: variables.VariableWarning{
Commands: map[string][]string{},
Components: map[string][]string{},
Projects: map[string][]string{},
StarterProjects: map[string][]string{},
},
additionalChecks: func(devfileObj parser.DevfileObj) error {
if devfileObj.Data.GetMetadata().DisplayName != "Test stack (devfile.yml)" {
return fmt.Errorf("expected 'Test stack (devfile.yml)' as metadata.displayName in devfile, but got %q",
devfileObj.Data.GetMetadata().DisplayName)
}
return nil
},
},
{
name: "parsing .devfile with .yml extension",
args: args{
args: parser.ParserArgs{
ExternalVariables: map[string]string{
"PARAMS": "from .devfile.yml",
},
Path: "./testdata/.devfile.yml",
},
},
wantCommandLine: "./main from .devfile.yml",
wantVariables: map[string]string{
"PARAMS": "from .devfile.yml",
},
wantVarWarning: variables.VariableWarning{
Commands: map[string][]string{},
Components: map[string][]string{},
Projects: map[string][]string{},
StarterProjects: map[string][]string{},
},
additionalChecks: func(devfileObj parser.DevfileObj) error {
if devfileObj.Data.GetMetadata().DisplayName != "Test stack (.devfile.yml)" {
return fmt.Errorf("expected 'Test stack (.devfile.yml)' as metadata.displayName in devfile, but got %q",
devfileObj.Data.GetMetadata().DisplayName)
}
return nil
},
},
{
name: "parsing .devfile with .yaml extension",
args: args{
args: parser.ParserArgs{
ExternalVariables: map[string]string{
"PARAMS": "from .devfile.yaml",
},
Path: "./testdata/.devfile.yaml",
},
},
wantCommandLine: "./main from .devfile.yaml",
wantVariables: map[string]string{
"PARAMS": "from .devfile.yaml",
},
wantVarWarning: variables.VariableWarning{
Commands: map[string][]string{},
Components: map[string][]string{},
Projects: map[string][]string{},
StarterProjects: map[string][]string{},
},
additionalChecks: func(devfileObj parser.DevfileObj) error {
if devfileObj.Data.GetMetadata().DisplayName != "Go Runtime (.devfile.yaml)" {
return fmt.Errorf("expected 'Go Runtime (.devfile.yaml)' as metadata.displayName in devfile, but got %q",
devfileObj.Data.GetMetadata().DisplayName)
}
return nil
},
},
{
name: "parsing any valid devfile regardless of extension",
args: args{
args: parser.ParserArgs{
ExternalVariables: map[string]string{
"PARAMS": "from any valid devfile file",
},
Path: "./testdata/valid-devfile.yaml.txt",
},
},
wantCommandLine: "./main from any valid devfile file",
wantVariables: map[string]string{
"PARAMS": "from any valid devfile file",
},
wantVarWarning: variables.VariableWarning{
Commands: map[string][]string{},
Components: map[string][]string{},
Projects: map[string][]string{},
StarterProjects: map[string][]string{},
},
additionalChecks: func(devfileObj parser.DevfileObj) error {
if devfileObj.Data.GetMetadata().DisplayName != "Test stack (valid-devfile.yaml.txt)" {
return fmt.Errorf("expected 'Test stack (valid-devfile.yaml.txt)' as metadata.displayName in devfile, but got %q",
devfileObj.Data.GetMetadata().DisplayName)
}
return nil
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down Expand Up @@ -647,6 +873,13 @@ spec:
if !reflect.DeepEqual(variables, tt.wantVariables) {
t.Errorf("variables are %+v, expected %+v", variables, tt.wantVariables)
}

if tt.additionalChecks != nil {
err = tt.additionalChecks(gotD)
if err != nil {
t.Errorf("unexpected error while performing specific checks: %v", err)
}
}
})
}
}
26 changes: 9 additions & 17 deletions pkg/devfile/parser/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,7 @@
package parser

import (
"fmt"
"net/url"
"os"
"path/filepath"
"strings"

"github.com/devfile/library/v2/pkg/testingutil/filesystem"
"github.com/devfile/library/v2/pkg/util"
Expand All @@ -36,7 +32,10 @@ type DevfileCtx struct {
// absolute path of devfile
absPath string

// relative path of devfile
// relative path of devfile.
// It can also be a relative or absolute path to a folder containing one or more devfiles,
// in which case the library will try to pick an existing one, based on the following priority order:
// devfile.yaml > .devfile.yaml > devfile.yml > .devfile.yml
relPath string

// raw content of the devfile
Expand Down Expand Up @@ -96,23 +95,16 @@ func (d *DevfileCtx) populateDevfile() (err error) {

// Populate fills the DevfileCtx struct with relevant context info
func (d *DevfileCtx) Populate() (err error) {
if !strings.HasSuffix(d.relPath, ".yaml") {
if _, err := os.Stat(filepath.Join(d.relPath, "devfile.yaml")); os.IsNotExist(err) {
if _, err := os.Stat(filepath.Join(d.relPath, ".devfile.yaml")); os.IsNotExist(err) {
return fmt.Errorf("the provided path is not a valid yaml filepath, and devfile.yaml or .devfile.yaml not found in the provided path : %s", d.relPath)
} else {
d.relPath = filepath.Join(d.relPath, ".devfile.yaml")
}
} else {
d.relPath = filepath.Join(d.relPath, "devfile.yaml")
}
d.relPath, err = lookupDevfileFromPath(d.fs, d.relPath)
if err != nil {
return err
}
if err := d.SetAbsPath(); err != nil {
if err = d.SetAbsPath(); err != nil {
return err
}
klog.V(4).Infof("absolute devfile path: '%s'", d.absPath)
// Read and save devfile content
if err := d.SetDevfileContent(); err != nil {
if err = d.SetDevfileContent(); err != nil {
return err
}
return d.populateDevfile()
Expand Down
Loading