From 395392c6f0a4b504a8d27378f442f66109817051 Mon Sep 17 00:00:00 2001 From: Best Olunusi Date: Tue, 20 Aug 2024 21:58:58 -0500 Subject: [PATCH 1/3] feat: add support for pkl files --- .github/workflows/golangci-lint.yml | 2 +- README.md | 5 +++-- cmd/validator/validator.go | 2 +- go.mod | 7 +++++-- go.sum | 8 ++++++++ index.md | 5 +++-- pkg/filetype/file_type.go | 9 ++++++++ pkg/validator/pkl.go | 32 +++++++++++++++++++++++++++++ pkg/validator/validator_test.go | 2 ++ test/fixtures/good.pkl | 7 +++++++ test/fixtures/subdir2/bad.pkl | 1 + 11 files changed, 72 insertions(+), 8 deletions(-) create mode 100644 pkg/validator/pkl.go create mode 100644 test/fixtures/good.pkl create mode 100644 test/fixtures/subdir2/bad.pkl diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 203e826b..bcedc8e1 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -15,7 +15,7 @@ jobs: golangci: strategy: matrix: - go: ['1.21'] + go: ['1.22'] os: [ubuntu-latest, macos-latest, windows-latest] permissions: # Optional: Allow write access to checks to allow the action to annotate code in the PR. diff --git a/README.md b/README.md index 7d1fbeed..26a11d1f 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ * INI * JSON * Properties +* PKL * TOML * XML * YAML @@ -136,7 +137,7 @@ validator --exclude-dirs=/path/to/search/tests /path/to/search ![Exclude Dirs Run](./img/exclude_dirs.png) #### Exclude file types -Exclude file types in the search path. Available file types are `csv`, `env`, `hcl`, `hocon`, `ini`, `json`, `plist`, `properties`, `toml`, `xml`, `yaml`, and `yml` +Exclude file types in the search path. Available file types are `csv`, `env`, `hcl`, `hocon`, `ini`, `json`, `plist`, `properties`, `pkl`, `toml`, `xml`, `yaml`, and `yml` ``` validator --exclude-file-types=json /path/to/search @@ -200,7 +201,7 @@ docker run -it --rm -v /path/to/config/files:/test config-file-validator:1.6.0 / ![Docker Standard Run](./img/docker_run.png) ## Build -The project can be downloaded and built from source using an environment with Go 1.21+ installed. After a successful build, the binary can be moved to a location on your operating system PATH. +The project can be downloaded and built from source using an environment with Go 1.22.1+ installed. After a successful build, the binary can be moved to a location on your operating system PATH. ### macOS #### Build diff --git a/cmd/validator/validator.go b/cmd/validator/validator.go index 63fceebc..36692f1b 100644 --- a/cmd/validator/validator.go +++ b/cmd/validator/validator.go @@ -2,7 +2,7 @@ Validator recursively scans a directory to search for configuration files and validates them using the go package for each configuration type. -Currently Apple PList XML, CSV, HCL, HOCON, INI, JSON, Properties, TOML, XML, and YAML. +Currently Apple PList XML, PKL, CSV, HCL, HOCON, INI, JSON, Properties, TOML, XML, and YAML. configuration file types are supported. Usage: validator [OPTIONS] [...] diff --git a/go.mod b/go.mod index 8d4165c0..6e762247 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/Boeing/config-file-validator -go 1.21 +go 1.22.1 require ( github.com/fatih/color v1.13.0 @@ -8,7 +8,7 @@ require ( github.com/hashicorp/hcl/v2 v2.18.1 github.com/magiconair/properties v1.8.7 github.com/pelletier/go-toml/v2 v2.0.6 - github.com/stretchr/testify v1.8.1 + github.com/stretchr/testify v1.9.0 gopkg.in/ini.v1 v1.67.0 gopkg.in/yaml.v3 v3.0.1 howett.net/plist v1.0.0 @@ -18,6 +18,7 @@ require ( github.com/agext/levenshtein v1.2.1 // indirect github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect + github.com/apple/pkl-go v0.8.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/editorconfig/editorconfig-core-go/v2 v2.6.2 // indirect github.com/google/go-cmp v0.6.0 // indirect @@ -26,6 +27,8 @@ require ( github.com/mattn/go-isatty v0.0.14 // indirect github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect + github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/zclconf/go-cty v1.13.0 // indirect golang.org/x/mod v0.16.0 // indirect golang.org/x/sync v0.2.0 // indirect diff --git a/go.sum b/go.sum index d15544fc..e18f6e10 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6 github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= +github.com/apple/pkl-go v0.8.0 h1:GRcBvFWeXjT9rc7A5gHK89qrel2wGZ3/a7ge4rPlT5M= +github.com/apple/pkl-go v0.8.0/go.mod h1:5Hwil5tyZGrOekh7JXLZJvIAcGHb4gT19lnv4WEiKeI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -49,6 +51,12 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= +github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= +github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= github.com/zclconf/go-cty v1.13.0 h1:It5dfKTTZHe9aeppbNOda3mN7Ag7sg6QkBNm6TkyFa0= github.com/zclconf/go-cty v1.13.0/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0= golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= diff --git a/index.md b/index.md index 2c740ee1..4551e851 100644 --- a/index.md +++ b/index.md @@ -60,6 +60,7 @@ * INI * JSON * Properties +* PKL * TOML * XML * YAML @@ -150,7 +151,7 @@ validator --exclude-dirs=/path/to/search/tests /path/to/search ![Exclude Dirs Run](./img/exclude_dirs.png) #### Exclude file types -Exclude file types in the search path. Available file types are `csv`, `env`, `hcl`, `hocon`, `ini`, `json`, `plist`, `properties`, `toml`, `xml`, `yaml`, and `yml` +Exclude file types in the search path. Available file types are `csv`, `env`, `hcl`, `hocon`, `ini`, `json`, `plist`, `properties`, `pkl`, `toml`, `xml`, `yaml`, and `yml` ``` validator --exclude-file-types=json /path/to/search @@ -214,7 +215,7 @@ docker run -it --rm -v /path/to/config/files:/test config-file-validator:1.6.0 / ![Docker Standard Run](./img/docker_run.png) ## Build -The project can be downloaded and built from source using an environment with Go 1.21+ installed. After a successful build, the binary can be moved to a location on your operating system PATH. +The project can be downloaded and built from source using an environment with Go 1.22.1+ installed. After a successful build, the binary can be moved to a location on your operating system PATH. ### macOS #### Build diff --git a/pkg/filetype/file_type.go b/pkg/filetype/file_type.go index 97f4c7bc..bd9fed4b 100644 --- a/pkg/filetype/file_type.go +++ b/pkg/filetype/file_type.go @@ -63,6 +63,14 @@ var PropFileType = FileType{ validator.PropValidator{}, } +// Instance of the FileType object to +// represent a Pkl file +var PklFileType = FileType{ + "pkl", + misc.ArrToMap("pkl"), + validator.PklValidator{}, +} + // Instance of the FileType object to // represent a HCL file var HclFileType = FileType{ @@ -120,6 +128,7 @@ var FileTypes = []FileType{ TomlFileType, IniFileType, PropFileType, + PklFileType, HclFileType, PlistFileType, CsvFileType, diff --git a/pkg/validator/pkl.go b/pkg/validator/pkl.go new file mode 100644 index 00000000..48b65007 --- /dev/null +++ b/pkg/validator/pkl.go @@ -0,0 +1,32 @@ +package validator + +import ( + "context" + "fmt" + + "github.com/apple/pkl-go/pkl" +) + +// PklValidator is used to validate a byte slice that is intended to represent a +// PKL file. +type PklValidator struct{} + +// Validate attempts to evaluate the provided byte slice as a PKL file. +func (PklValidator) Validate(b []byte) (bool, error) { + ctx := context.Background() + + // Convert the byte slice to a ModuleSource using TextSource + source := pkl.TextSource(string(b)) + + evaluator, err := pkl.NewEvaluator(ctx, pkl.PreconfiguredOptions) + if err != nil { + return false, fmt.Errorf("failed to create evaluator: %w", err) + } + + _, err = evaluator.EvaluateExpressionRaw(ctx, source, "") + if err != nil { + return false, fmt.Errorf("failed to evaluate module: %w", err) + } + + return true, nil +} diff --git a/pkg/validator/validator_test.go b/pkg/validator/validator_test.go index 210af41d..93b43d40 100644 --- a/pkg/validator/validator_test.go +++ b/pkg/validator/validator_test.go @@ -55,6 +55,8 @@ var testData = []struct { {"invalidIni", []byte(`\nCatalog hidden\n`), false, IniValidator{}}, {"validProperties", []byte("key=value\nkey2=${key}"), true, PropValidator{}}, {"invalidProperties", []byte("key=${key}"), false, PropValidator{}}, + {"validPkl", []byte(`name = "Swallow"`), true, PklValidator{}}, + {"invalidPkl", []byte(`"name" = "Swallow"`), false, PklValidator{}}, {"validHcl", []byte(`key = "value"`), true, HclValidator{}}, {"invalidHcl", []byte(`"key" = "value"`), false, HclValidator{}}, {"multipleInvalidHcl", []byte(`"key1" = "value1"\n"key2"="value2"`), false, HclValidator{}}, diff --git a/test/fixtures/good.pkl b/test/fixtures/good.pkl new file mode 100644 index 00000000..9d4c336a --- /dev/null +++ b/test/fixtures/good.pkl @@ -0,0 +1,7 @@ +name = "Swallow" + +job { + title = "Sr. Nest Maker" + company = "Nests R Us" + yearsOfExperience = 2 +} diff --git a/test/fixtures/subdir2/bad.pkl b/test/fixtures/subdir2/bad.pkl new file mode 100644 index 00000000..50a269a4 --- /dev/null +++ b/test/fixtures/subdir2/bad.pkl @@ -0,0 +1 @@ +"invalid" From b706fe9005e74fbd706b88c10ecc40ff85d0111f Mon Sep 17 00:00:00 2001 From: Best Olunusi Date: Sun, 1 Sep 2024 19:52:50 -0500 Subject: [PATCH 2/3] feat: skip pkl files when binary isn't in PATH --- .github/workflows/go.yml | 5 +++++ README.md | 2 +- index.md | 2 +- pkg/cli/cli.go | 15 +++++++++++++++ pkg/cli/cli_test.go | 27 +++++++++++++++++++++++++++ pkg/validator/validator_test.go | 13 +++++++++++++ 6 files changed, 62 insertions(+), 2 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index edabd2ff..82421109 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -97,6 +97,11 @@ jobs: with: go-version: '1.22' + - name: Set up pkl + uses: pkl-community/setup-pkl@5bb6ac805eb51c448837ec34e9957a18adab927d # main + with: + pkl-version: '0.26.3' + - name: Unit test run: go test -v -cover -coverprofile coverage.out ./... diff --git a/README.md b/README.md index 26a11d1f..5e765f49 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ * INI * JSON * Properties -* PKL +* PKL _(requires that the `pkl` binary is installed; `.pkl` files will be ignored if the binary is not installed)_ * TOML * XML * YAML diff --git a/index.md b/index.md index 4551e851..306c5c38 100644 --- a/index.md +++ b/index.md @@ -60,7 +60,7 @@ * INI * JSON * Properties -* PKL +* PKL _(requires that the `pkl` binary is installed; `.pkl` files will be ignored if the binary is not installed)_ * TOML * XML * YAML diff --git a/pkg/cli/cli.go b/pkg/cli/cli.go index 3a702074..30aac387 100644 --- a/pkg/cli/cli.go +++ b/pkg/cli/cli.go @@ -3,6 +3,7 @@ package cli import ( "fmt" "os" + "os/exec" "github.com/Boeing/config-file-validator/pkg/finder" "github.com/Boeing/config-file-validator/pkg/reporter" @@ -73,6 +74,12 @@ func Init(opts ...Option) *CLI { return cli } +// Check if the 'pkl' binary is present in the PATH +var isPklBinaryPresent = func() bool { + _, err := exec.LookPath("pkl") + return err == nil +} + // The Run method performs the following actions: // - Finds the calls the Find method from the Finder interface to // return a list of files @@ -88,6 +95,14 @@ func (c CLI) Run() (int, error) { } for _, fileToValidate := range foundFiles { + // Pkl validation requires the 'pkl' binary to be present + if fileToValidate.FileType.Name == "pkl" { + if !isPklBinaryPresent() { + fmt.Printf("Warning: 'pkl' binary not found, file %s will be ignored.\n", fileToValidate.Path) + continue + } + } + // read it fileContent, err := os.ReadFile(fileToValidate.Path) if err != nil { diff --git a/pkg/cli/cli_test.go b/pkg/cli/cli_test.go index 87a1fdfb..4257fc7a 100644 --- a/pkg/cli/cli_test.go +++ b/pkg/cli/cli_test.go @@ -121,3 +121,30 @@ func Test_CLIRepoertErr(t *testing.T) { t.Errorf("should return err status code: %d", exitStatus) } } + +func Test_CLI_IgnoreBadPklFileWhenBinaryNotFound(t *testing.T) { + // Save the original function before mocking and restore it after the test + originalIsPklBinaryPresent := isPklBinaryPresent + isPklBinaryPresent = func() bool { + return false + } + defer func() { isPklBinaryPresent = originalIsPklBinaryPresent }() + + searchPath := "../../test/fixtures/subdir2/bad.pkl" + fsFinder := finder.FileSystemFinderInit( + finder.WithPathRoots(searchPath), + ) + cli := Init( + WithFinder(fsFinder), + ) + exitStatus, err := cli.Run() + if err != nil { + t.Errorf("An error was returned: %v", err) + } + + // Since the pkl binary is not found, the bad pkl file should be ignored + // So the exit status should be 0 + if exitStatus != 0 { + t.Errorf("Expected exit status 0, but got: %d", exitStatus) + } +} diff --git a/pkg/validator/validator_test.go b/pkg/validator/validator_test.go index 93b43d40..28767b2f 100644 --- a/pkg/validator/validator_test.go +++ b/pkg/validator/validator_test.go @@ -2,6 +2,7 @@ package validator import ( _ "embed" + "os/exec" "testing" ) @@ -72,6 +73,11 @@ var testData = []struct { {"invalidEditorConfig", []byte("[*.md\nworking=false"), false, EditorConfigValidator{}}, } +func isPklBinaryPresent() bool { + _, err := exec.LookPath("pkl") + return err == nil +} + func Test_ValidationInput(t *testing.T) { t.Parallel() @@ -81,6 +87,13 @@ func Test_ValidationInput(t *testing.T) { t.Run(tcase.name, func(t *testing.T) { t.Parallel() + // Skip PklValidator tests if the pkl binary is not present + if _, ok := tcase.validator.(PklValidator); ok { + if !isPklBinaryPresent() { + t.Skip("Skipping test: 'pkl' binary not found.") + } + } + valid, err := tcase.validator.Validate(tcase.testInput) if valid != tcase.expectedResult { t.Errorf("incorrect result: expected %v, got %v", tcase.expectedResult, valid) From 1fb119bacd1157138863b12e94007fd447860210 Mon Sep 17 00:00:00 2001 From: Best Olunusi Date: Mon, 2 Sep 2024 16:16:38 -0500 Subject: [PATCH 3/3] chore: use the new action tag in ref comment --- .github/workflows/go.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 82421109..a533a2b9 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -98,7 +98,7 @@ jobs: go-version: '1.22' - name: Set up pkl - uses: pkl-community/setup-pkl@5bb6ac805eb51c448837ec34e9957a18adab927d # main + uses: pkl-community/setup-pkl@5bb6ac805eb51c448837ec34e9957a18adab927d # v0.0.5 with: pkl-version: '0.26.3'