From 6562208897c6b7276b3318e03e083b551b5c39f3 Mon Sep 17 00:00:00 2001 From: David Moore Date: Wed, 12 Jun 2024 12:27:06 +1000 Subject: [PATCH] feat: ability to lint and validate the nitric.yaml from json schema --- cmd/root.go | 7 +++ pkg/paths/paths.go | 5 ++ pkg/schemas/nitric-yaml-schema.json | 85 ++++++++++++++++++++++++++ pkg/schemas/schemas.go | 93 +++++++++++++++++++++++++++++ 4 files changed, 190 insertions(+) create mode 100644 pkg/schemas/nitric-yaml-schema.json create mode 100644 pkg/schemas/schemas.go diff --git a/cmd/root.go b/cmd/root.go index 194619816..d1108c65c 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -25,6 +25,7 @@ import ( "github.com/spf13/cobra" "github.com/nitrictech/cli/pkg/paths" + "github.com/nitrictech/cli/pkg/schemas" "github.com/nitrictech/cli/pkg/update" "github.com/nitrictech/cli/pkg/view/tui" ) @@ -72,6 +73,12 @@ var rootCmd = &cobra.Command{ update.PrintOutdatedWarning() // an unstyled \n is always needed at the end of the view to ensure the last line renders fmt.Println() + + // Check/install schemas + err := schemas.Install() + if err != nil { + tui.CheckErr(fmt.Errorf("Failed to create nitric schema. %w", err)) + } }, } diff --git a/pkg/paths/paths.go b/pkg/paths/paths.go index c42761508..62dce948c 100644 --- a/pkg/paths/paths.go +++ b/pkg/paths/paths.go @@ -65,6 +65,11 @@ func NitricTemplatesDir() string { return filepath.Join(NitricHomeDir(), "store") } +// NitricSchemasDir returns the directory to place schema related data. +func NitricSchemasDir() string { + return filepath.Join(NitricHomeDir(), "schemas") +} + func NitricStacksDir() (string, error) { homeDir := NitricHomeDir() stacksDir := path.Join(homeDir, "stacks") diff --git a/pkg/schemas/nitric-yaml-schema.json b/pkg/schemas/nitric-yaml-schema.json new file mode 100644 index 000000000..3900acf37 --- /dev/null +++ b/pkg/schemas/nitric-yaml-schema.json @@ -0,0 +1,85 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "{{.Version}}", + "title": "JSON schema for the Nitric configuration file", + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "description": "The name of the project." + }, + "directory": { + "type": "string", + "description": "The directory of the project." + }, + "services": { + "type": "array", + "description": "A list of service configurations.", + "minItems": 1, + "items": { + "type": "object", + "properties": { + "match": { + "type": "string", + "description": "The file pattern to match service files." + }, + "runtime": { + "type": "string", + "description": "This is the custom runtime version (is custom if not nil, we auto-detect a standard language runtime)." + }, + "start": { + "type": "string", + "description": "The command to start the service. A $SERVICE_PATH environment variable is available which will specify the relative filepath of each matched service." + }, + "type": { + "type": "string", + "description": "The type of the service.", + "enum": ["default", "memory-optimized"] + } + }, + "required": ["match", "start"] + } + }, + "runtimes": { + "type": "object", + "description": "A map of runtime configurations.", + "additionalProperties": { + "type": "object", + "properties": { + "dockerfile": { + "type": "string", + "description": "The path to the Dockerfile for this runtime." + }, + "args": { + "type": "object", + "description": "Arguments for the Docker build.", + "additionalProperties": { + "type": "string" + } + } + }, + "required": ["dockerfile"] + } + }, + "preview": { + "type": "array", + "minItems": 1, + "description": "A list of preview features to enable. Checkout https://nitric.io/docs/reference/preview-features for the latest preview features.", + "items": { + "type": "string", + "oneOf": [ + { + "const": "docker-providers", + "description": "Use docker containers to distribute nitric providers." + }, + { + "const": "beta-providers", + "description": "Use nitric providers that are currently in beta." + } + ] + } + } + }, + "required": ["name", "services"] +} diff --git a/pkg/schemas/schemas.go b/pkg/schemas/schemas.go new file mode 100644 index 000000000..112f3270b --- /dev/null +++ b/pkg/schemas/schemas.go @@ -0,0 +1,93 @@ +// Copyright Nitric Pty Ltd. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package schemas + +import ( + "bytes" + _ "embed" + "fmt" + "html/template" + "os" + "path/filepath" + + "github.com/nitrictech/cli/pkg/paths" + "github.com/nitrictech/cli/pkg/version" +) + +//go:embed nitric-yaml-schema.json +var nitricYamlSchemaTemplate string + +// NewProvider - Returns a new provider instance based on the given providerId string +// The providerId string is in the form of /@ +func Install() error { + currentVersion := version.Version + dir := paths.NitricSchemasDir() + filePath := filepath.Join(dir, "nitric-yaml-schema.json") + versionFilePath := filepath.Join(dir, "version.lock") + + // Ensure the Nitric Schemas Directory Exists + if _, err := os.Stat(dir); os.IsNotExist(err) { + err := os.MkdirAll(dir, 0o700) + if err != nil { + return fmt.Errorf("failed to create nitric schemas directory. %w", err) + } + } + + // Read the existing version from the version file, if it exists + storedVersion, err := os.ReadFile(versionFilePath) + if err == nil { + // Remove trailing newline for comparison + storedVersion = bytes.TrimSpace(storedVersion) + } + + // Check if the stored version matches the current version + if string(storedVersion) == currentVersion { + // Versions are the same, no need to update + return nil + } + + // Prepare the template with the current version + tmpl, err := template.New("schema").Parse(nitricYamlSchemaTemplate) + if err != nil { + return fmt.Errorf("failed to parse nitric schema template: %w", err) + } + + var content bytes.Buffer + + err = tmpl.Execute(&content, struct { + Version string + }{ + Version: currentVersion, + }) + if err != nil { + return fmt.Errorf("failed to execute template for nitric schema file: %w", err) + } + + // Write the new content to the schema file + err = os.WriteFile(filePath, content.Bytes(), 0o644) + if err != nil { + return fmt.Errorf("failed to write nitric schema file: %w", err) + } + + // Write the new version lock + err = os.WriteFile(versionFilePath, []byte(currentVersion+"\n"), 0o644) + if err != nil { + return fmt.Errorf("failed to write nitric schema version lock file: %w", err) + } + + return nil +}