Skip to content

Commit 57a7da8

Browse files
authored
Make the parser able to parse any file (including files with .yml extension)
This merges pull request #186 from rm3l/1265-support-.yml-extension Signed-off-by: Armel Soro <[email protected]>
2 parents 36607c5 + 728befe commit 57a7da8

File tree

19 files changed

+918
-83
lines changed

19 files changed

+918
-83
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
## About
99

1010
The Devfile Parser library is a Golang module that:
11-
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).
12-
2. writes to the devfile.yaml with the updated data.
11+
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).
12+
2. writes to the specified devfile with the updated data.
1313
3. generates Kubernetes objects for the various devfile resources.
1414
4. defines util functions for the devfile.
15-
5. downloads resources from a parent devfile if specified in the devfile.yaml
15+
5. downloads resources from a parent devfile if specified in the devfile.
1616

1717
## Private repository support
1818

pkg/devfile/parse_test.go

Lines changed: 234 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package devfile
1717

1818
import (
1919
"context"
20+
"fmt"
2021
"net"
2122
"net/http"
2223
"net/http/httptest"
@@ -273,6 +274,7 @@ spec:
273274
wantKubernetesInline string
274275
wantOpenshiftInline string
275276
wantVariables map[string]string
277+
additionalChecks func(parser.DevfileObj) error
276278
}{
277279
{
278280
name: "with external overriding variables",
@@ -415,7 +417,7 @@ spec:
415417
ExternalVariables: map[string]string{
416418
"PARAMS": "baz",
417419
},
418-
Path: "./testdata/devfile1.yaml",
420+
Path: "./testdata/devfile.yaml",
419421
},
420422
},
421423
wantCommandLine: "./main baz",
@@ -527,6 +529,230 @@ spec:
527529
StarterProjects: map[string][]string{},
528530
},
529531
},
532+
{
533+
name: "parsing devfile with context path containing multiple devfiles => priority to devfile.yaml",
534+
args: args{
535+
args: parser.ParserArgs{
536+
ExternalVariables: map[string]string{
537+
"PARAMS": "from devfile.yaml based on priority",
538+
},
539+
Path: "./testdata",
540+
},
541+
},
542+
wantCommandLine: "./main from devfile.yaml based on priority",
543+
wantVariables: map[string]string{
544+
"PARAMS": "from devfile.yaml based on priority",
545+
},
546+
wantVarWarning: variables.VariableWarning{
547+
Commands: map[string][]string{},
548+
Components: map[string][]string{},
549+
Projects: map[string][]string{},
550+
StarterProjects: map[string][]string{},
551+
},
552+
additionalChecks: func(devfileObj parser.DevfileObj) error {
553+
if devfileObj.Data.GetMetadata().DisplayName != "Go Runtime (devfile.yaml)" {
554+
return fmt.Errorf("expected 'Go Runtime (devfile.yaml)' as metadata.displayName in devfile, but got %q",
555+
devfileObj.Data.GetMetadata().DisplayName)
556+
}
557+
return nil
558+
},
559+
},
560+
{
561+
name: "parsing devfile with context path containing multiple devfiles => priority to .devfile.yaml",
562+
args: args{
563+
args: parser.ParserArgs{
564+
ExternalVariables: map[string]string{
565+
"PARAMS": "from .devfile.yaml based on priority",
566+
},
567+
Path: "./testdata/priority-for-dot_devfile_yaml",
568+
},
569+
},
570+
wantCommandLine: "./main from .devfile.yaml based on priority",
571+
wantVariables: map[string]string{
572+
"PARAMS": "from .devfile.yaml based on priority",
573+
},
574+
wantVarWarning: variables.VariableWarning{
575+
Commands: map[string][]string{},
576+
Components: map[string][]string{},
577+
Projects: map[string][]string{},
578+
StarterProjects: map[string][]string{},
579+
},
580+
additionalChecks: func(devfileObj parser.DevfileObj) error {
581+
if devfileObj.Data.GetMetadata().DisplayName != "Go Runtime (.devfile.yaml)" {
582+
return fmt.Errorf("expected 'Go Runtime (.devfile.yaml)' as metadata.displayName in devfile, but got %q",
583+
devfileObj.Data.GetMetadata().DisplayName)
584+
}
585+
return nil
586+
},
587+
},
588+
{
589+
name: "parsing devfile with context path containing multiple devfiles => priority to devfile.yml",
590+
args: args{
591+
args: parser.ParserArgs{
592+
ExternalVariables: map[string]string{
593+
"PARAMS": "from devfile.yml based on priority",
594+
},
595+
Path: "./testdata/priority-for-devfile_yml",
596+
},
597+
},
598+
wantCommandLine: "./main from devfile.yml based on priority",
599+
wantVariables: map[string]string{
600+
"PARAMS": "from devfile.yml based on priority",
601+
},
602+
wantVarWarning: variables.VariableWarning{
603+
Commands: map[string][]string{},
604+
Components: map[string][]string{},
605+
Projects: map[string][]string{},
606+
StarterProjects: map[string][]string{},
607+
},
608+
additionalChecks: func(devfileObj parser.DevfileObj) error {
609+
if devfileObj.Data.GetMetadata().DisplayName != "Test stack (devfile.yml)" {
610+
return fmt.Errorf("expected 'Test stack (devfile.yml)' as metadata.displayName in devfile, but got %q",
611+
devfileObj.Data.GetMetadata().DisplayName)
612+
}
613+
return nil
614+
},
615+
},
616+
{
617+
name: "parsing devfile with context path containing multiple devfiles => priority to .devfile.yml",
618+
args: args{
619+
args: parser.ParserArgs{
620+
ExternalVariables: map[string]string{
621+
"PARAMS": "from .devfile.yml based on priority",
622+
},
623+
Path: "./testdata/priority-for-dot_devfile_yml",
624+
},
625+
},
626+
wantCommandLine: "./main from .devfile.yml based on priority",
627+
wantVariables: map[string]string{
628+
"PARAMS": "from .devfile.yml based on priority",
629+
},
630+
wantVarWarning: variables.VariableWarning{
631+
Commands: map[string][]string{},
632+
Components: map[string][]string{},
633+
Projects: map[string][]string{},
634+
StarterProjects: map[string][]string{},
635+
},
636+
additionalChecks: func(devfileObj parser.DevfileObj) error {
637+
if devfileObj.Data.GetMetadata().DisplayName != "Test stack (.devfile.yml)" {
638+
return fmt.Errorf("expected 'Test stack (.devfile.yml)' as metadata.displayName in devfile, but got %q",
639+
devfileObj.Data.GetMetadata().DisplayName)
640+
}
641+
return nil
642+
},
643+
},
644+
{
645+
name: "parsing devfile with .yml extension",
646+
args: args{
647+
args: parser.ParserArgs{
648+
ExternalVariables: map[string]string{
649+
"PARAMS": "from devfile.yml",
650+
},
651+
Path: "./testdata/devfile.yml",
652+
},
653+
},
654+
wantCommandLine: "./main from devfile.yml",
655+
wantVariables: map[string]string{
656+
"PARAMS": "from devfile.yml",
657+
},
658+
wantVarWarning: variables.VariableWarning{
659+
Commands: map[string][]string{},
660+
Components: map[string][]string{},
661+
Projects: map[string][]string{},
662+
StarterProjects: map[string][]string{},
663+
},
664+
additionalChecks: func(devfileObj parser.DevfileObj) error {
665+
if devfileObj.Data.GetMetadata().DisplayName != "Test stack (devfile.yml)" {
666+
return fmt.Errorf("expected 'Test stack (devfile.yml)' as metadata.displayName in devfile, but got %q",
667+
devfileObj.Data.GetMetadata().DisplayName)
668+
}
669+
return nil
670+
},
671+
},
672+
{
673+
name: "parsing .devfile with .yml extension",
674+
args: args{
675+
args: parser.ParserArgs{
676+
ExternalVariables: map[string]string{
677+
"PARAMS": "from .devfile.yml",
678+
},
679+
Path: "./testdata/.devfile.yml",
680+
},
681+
},
682+
wantCommandLine: "./main from .devfile.yml",
683+
wantVariables: map[string]string{
684+
"PARAMS": "from .devfile.yml",
685+
},
686+
wantVarWarning: variables.VariableWarning{
687+
Commands: map[string][]string{},
688+
Components: map[string][]string{},
689+
Projects: map[string][]string{},
690+
StarterProjects: map[string][]string{},
691+
},
692+
additionalChecks: func(devfileObj parser.DevfileObj) error {
693+
if devfileObj.Data.GetMetadata().DisplayName != "Test stack (.devfile.yml)" {
694+
return fmt.Errorf("expected 'Test stack (.devfile.yml)' as metadata.displayName in devfile, but got %q",
695+
devfileObj.Data.GetMetadata().DisplayName)
696+
}
697+
return nil
698+
},
699+
},
700+
{
701+
name: "parsing .devfile with .yaml extension",
702+
args: args{
703+
args: parser.ParserArgs{
704+
ExternalVariables: map[string]string{
705+
"PARAMS": "from .devfile.yaml",
706+
},
707+
Path: "./testdata/.devfile.yaml",
708+
},
709+
},
710+
wantCommandLine: "./main from .devfile.yaml",
711+
wantVariables: map[string]string{
712+
"PARAMS": "from .devfile.yaml",
713+
},
714+
wantVarWarning: variables.VariableWarning{
715+
Commands: map[string][]string{},
716+
Components: map[string][]string{},
717+
Projects: map[string][]string{},
718+
StarterProjects: map[string][]string{},
719+
},
720+
additionalChecks: func(devfileObj parser.DevfileObj) error {
721+
if devfileObj.Data.GetMetadata().DisplayName != "Go Runtime (.devfile.yaml)" {
722+
return fmt.Errorf("expected 'Go Runtime (.devfile.yaml)' as metadata.displayName in devfile, but got %q",
723+
devfileObj.Data.GetMetadata().DisplayName)
724+
}
725+
return nil
726+
},
727+
},
728+
{
729+
name: "parsing any valid devfile regardless of extension",
730+
args: args{
731+
args: parser.ParserArgs{
732+
ExternalVariables: map[string]string{
733+
"PARAMS": "from any valid devfile file",
734+
},
735+
Path: "./testdata/valid-devfile.yaml.txt",
736+
},
737+
},
738+
wantCommandLine: "./main from any valid devfile file",
739+
wantVariables: map[string]string{
740+
"PARAMS": "from any valid devfile file",
741+
},
742+
wantVarWarning: variables.VariableWarning{
743+
Commands: map[string][]string{},
744+
Components: map[string][]string{},
745+
Projects: map[string][]string{},
746+
StarterProjects: map[string][]string{},
747+
},
748+
additionalChecks: func(devfileObj parser.DevfileObj) error {
749+
if devfileObj.Data.GetMetadata().DisplayName != "Test stack (valid-devfile.yaml.txt)" {
750+
return fmt.Errorf("expected 'Test stack (valid-devfile.yaml.txt)' as metadata.displayName in devfile, but got %q",
751+
devfileObj.Data.GetMetadata().DisplayName)
752+
}
753+
return nil
754+
},
755+
},
530756
}
531757
for _, tt := range tests {
532758
t.Run(tt.name, func(t *testing.T) {
@@ -647,6 +873,13 @@ spec:
647873
if !reflect.DeepEqual(variables, tt.wantVariables) {
648874
t.Errorf("variables are %+v, expected %+v", variables, tt.wantVariables)
649875
}
876+
877+
if tt.additionalChecks != nil {
878+
err = tt.additionalChecks(gotD)
879+
if err != nil {
880+
t.Errorf("unexpected error while performing specific checks: %v", err)
881+
}
882+
}
650883
})
651884
}
652885
}

pkg/devfile/parser/context/context.go

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,7 @@
1616
package parser
1717

1818
import (
19-
"fmt"
2019
"net/url"
21-
"os"
22-
"path/filepath"
23-
"strings"
2420

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

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

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

9796
// Populate fills the DevfileCtx struct with relevant context info
9897
func (d *DevfileCtx) Populate() (err error) {
99-
if !strings.HasSuffix(d.relPath, ".yaml") {
100-
if _, err := os.Stat(filepath.Join(d.relPath, "devfile.yaml")); os.IsNotExist(err) {
101-
if _, err := os.Stat(filepath.Join(d.relPath, ".devfile.yaml")); os.IsNotExist(err) {
102-
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)
103-
} else {
104-
d.relPath = filepath.Join(d.relPath, ".devfile.yaml")
105-
}
106-
} else {
107-
d.relPath = filepath.Join(d.relPath, "devfile.yaml")
108-
}
98+
d.relPath, err = lookupDevfileFromPath(d.fs, d.relPath)
99+
if err != nil {
100+
return err
109101
}
110-
if err := d.SetAbsPath(); err != nil {
102+
if err = d.SetAbsPath(); err != nil {
111103
return err
112104
}
113105
klog.V(4).Infof("absolute devfile path: '%s'", d.absPath)
114106
// Read and save devfile content
115-
if err := d.SetDevfileContent(); err != nil {
107+
if err = d.SetDevfileContent(); err != nil {
116108
return err
117109
}
118110
return d.populateDevfile()

0 commit comments

Comments
 (0)