diff --git a/internal/package_handler/schema.go b/internal/package_handler/schema.go index 7f6262dc..04e6ff8d 100644 --- a/internal/package_handler/schema.go +++ b/internal/package_handler/schema.go @@ -1,17 +1,30 @@ package package_handler import ( + "embed" "encoding/json" "fmt" + "log" "os" + "path/filepath" + "github.com/go-git/go-git/v5" "github.com/xeipuuv/gojsonschema" "gopkg.in/yaml.v3" ) +const ( + manifestSchema = "schema/manifest_schema.yml" + profileSchema = "schema/profile_schema.yml" +) + +//go:embed schema/* +var fs embed.FS + func validateYAMLSchema(schemaFile, documentFile string) error { // Read the YAML schema - schemaData, err := os.ReadFile(schemaFile) + + schemaData, err := fs.ReadFile(schemaFile) if err != nil { return fmt.Errorf("error reading the schema file: %v", err) } @@ -57,3 +70,75 @@ func validateYAMLSchema(schemaFile, documentFile string) error { } return nil } + +func ValidateFromRepository(repoURL, repoPath string) (err error) { + err = cloneRepository(repoURL, repoPath) + if err != nil { + log.Fatal("Failed to clone repository:", err) + } + // Walk through the directories and validate YAML files + err = walkDirectories(repoPath) + if err != nil { + log.Fatal("Failed to walk directories:", err) + } + + // Remove repoPath + err = os.RemoveAll(repoPath) + if err != nil { + log.Fatal(err) + } + + return nil +} + +func cloneRepository(url string, path string) error { + _, err := git.PlainClone(path, false, &git.CloneOptions{ + URL: url, + }) + if err != nil { + log.Println("Failed to clone repository:", err) + return err + } + return nil +} + +func walkDirectories(root string) error { + err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { + if err != nil { + log.Println("Failed to walk directory:", err) + return err + } + + // Check if the file is a 'profile.yml' or 'manifest.yml' + if info.IsDir() { + return nil + } + + if info.Name() == "profile.yml" { + // Validate the YAML file against the schema + err := validateYAMLSchema(profileSchema, path) + if err != nil { + log.Printf("Failed to validate %s: %s\n", path, err) + } else { + log.Printf("%s is valid\n", path) + } + } + if info.Name() == "manifest.yml" { + // Validate the YAML file against the schema + err := validateYAMLSchema(manifestSchema, path) + if err != nil { + log.Printf("Failed to validate %s: %s\n", path, err) + } else { + log.Printf("%s is valid\n", path) + } + } + + return nil + }) + if err != nil { + log.Println("Failed to walk directories:", err) + return err + } + + return nil +} diff --git a/internal/package_handler/schema/manifest_schema.yml b/internal/package_handler/schema/manifest_schema.yml index 27d3d40c..a839a734 100644 --- a/internal/package_handler/schema/manifest_schema.yml +++ b/internal/package_handler/schema/manifest_schema.yml @@ -33,8 +33,11 @@ properties: image: type: string additionalProperties: false + required: + - image profiles: type: array + minItems: 1 items: type: string required: diff --git a/internal/package_handler/schema/profile_schema.yml b/internal/package_handler/schema/profile_schema.yml index c3711213..7e3b2a46 100644 --- a/internal/package_handler/schema/profile_schema.yml +++ b/internal/package_handler/schema/profile_schema.yml @@ -1,4 +1,260 @@ $schema: "http://json-schema.org/draft-07/schema#" +# Definitions of all type of Option +definitions: + OptionInt: + type: object + properties: + name: + type: string + target: + type: string + help: + type: string + type: + const: "int" + default: + type: integer + validate: + type: object + properties: + min_value: + type: integer + max_value: + type: integer + required: + - min_value + - max_value + additionalProperties: false + required: + - name + - target + - help + - type + additionalProperties: false + OptionString: + type: object + properties: + name: + type: string + target: + type: string + help: + type: string + type: + const: "str" + default: + type: string + validate: + type: object + properties: + re2_regex: + type: string + required: + - re2_regex + additionalProperties: false + required: + - name + - target + - help + - type + additionalProperties: false + OptionFloat: + type: object + properties: + name: + type: string + target: + type: string + help: + type: string + type: + const: "float" + default: + type: number + validate: + type: object + properties: + min_value: + type: number + max_value: + type: number + required: + - min_value + - max_value + additionalProperties: false + required: + - name + - target + - help + - type + additionalProperties: false + OptionBool: + type: object + properties: + name: + type: string + target: + type: string + help: + type: string + type: + const: "bool" + default: + type: boolean + required: + - name + - target + - help + - type + additionalProperties: false + OptionPathDir: + type: object + properties: + name: + type: string + target: + type: string + help: + type: string + type: + const: "path_dir" + default: + type: string + required: + - name + - target + - help + - type + additionalProperties: false + OptionPathFile: + type: object + properties: + name: + type: string + target: + type: string + help: + type: string + type: + const: "path_file" + default: + type: string + validate: + type: object + properties: + format: + type: string + required: + - format + additionalProperties: false + required: + - name + - target + - help + - type + additionalProperties: false + OptionURI: + type: object + properties: + name: + type: string + target: + type: string + help: + type: string + type: + const: "uri" + default: + type: string + validate: + type: object + properties: + uri_scheme: + type: array + minItems: 1 + items: + type: string + required: + - uri_scheme + additionalProperties: false + required: + - name + - target + - help + - type + additionalProperties: false + OptionSelect: + type: object + properties: + name: + type: string + target: + type: string + help: + type: string + type: + const: "select" + default: + type: string + validate: + type: object + properties: + options: + type: array + minItems: 1 + items: + type: string + required: + - options + additionalProperties: false + required: + - name + - target + - help + - type + additionalProperties: false + OptionPort: + type: object + properties: + name: + type: string + target: + type: string + help: + type: string + type: + const: "port" + default: + type: integer + minumum: 1 + maximum: 65535 + required: + - name + - target + - help + - type + additionalProperties: false + OptionID: + type: object + properties: + name: + type: string + target: + type: string + help: + type: string + type: + const: "id" + default: + type: string + required: + - name + - target + - help + - type + additionalProperties: false + +# Profile Schema type: object properties: name: @@ -34,43 +290,17 @@ properties: options: type: array items: - type: object - properties: - name: - type: string - target: - type: string - type: - type: string - default: {} #TODO: Add minimun_count = 1 - help: - type: string - validate: - type: object - properties: - re2_regex: - type: string - format: - type: string - uri_scheme: - type: array - items: - type: string - min_value: - type: number - max_value: - type: number - options: - type: array - items: - type: string - additionalProperties: false - required: - - name - - target - - type - - help - additionalProperties: false + oneOf: + - $ref: '#/definitions/OptionInt' + - $ref: '#/definitions/OptionString' + - $ref: '#/definitions/OptionFloat' + - $ref: '#/definitions/OptionBool' + - $ref: '#/definitions/OptionPathDir' + - $ref: '#/definitions/OptionPathFile' + - $ref: '#/definitions/OptionURI' + - $ref: '#/definitions/OptionSelect' + - $ref: '#/definitions/OptionPort' + - $ref: '#/definitions/OptionID' monitoring: type: object properties: diff --git a/internal/package_handler/schema_test.go b/internal/package_handler/schema_test.go index 3ee8a802..614905a5 100644 --- a/internal/package_handler/schema_test.go +++ b/internal/package_handler/schema_test.go @@ -1,6 +1,10 @@ package package_handler -import "testing" +import ( + "fmt" + "log" + "testing" +) func Test_validateYAMLSchema(t *testing.T) { type args struct { @@ -53,3 +57,16 @@ func Test_validateYAMLSchema(t *testing.T) { }) } } + +func ExampleValidateFromRepository() { + repoURL := "https://github.com/NethermindEth/mock-avs/" + repoPath := "testdata/temp/" + + err := ValidateFromRepository(repoURL, repoPath) + if err != nil { + // panic(err) + log.Fatal("Failed to check repository: ", repoURL, err) + } + fmt.Println("3 passed - 1 failed") + // Output: 3 passed - 1 failed +}