Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Monkey patch yaml handling #3

Merged
merged 12 commits into from
Dec 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ jobs:
run: |
& '.\install.ps1'
& '.\build.ps1'
- name: Archive production artifacts
uses: actions/upload-artifact@v2
with:
name: DistFolder
path: dist
- name: Upload Release Asset
if: startsWith(github.ref, 'refs/tags/')
env:
Expand Down
25 changes: 25 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Build Validator",
"command": "./build.ps1",
"type": "shell",
"problemMatcher": [],
"presentation": {
"reveal": "always",
},
"group": "build",
},
{
"label": "Install dependencies",
"command": "./install.ps1",
"type": "shell",
"problemMatcher": [],
"presentation": {
"reveal": "always",
},
"group": "build",
},
],
}
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
# Validator

A small program that validates JSON files against a JSON Schema. It's mainly used for automated tests.

This is a helper program for [Scoop](https://scoop.sh), the Windows command-line installer.
A small program that validates JSON/YAML manifests against a JSON Schema. It's mainly used for automated tests.

## Usage

```pwsh
validator.exe <schema> <manifest> [<manifest>...]
validator.exe <SCHEMA> <MANIFEST>...
```
7 changes: 5 additions & 2 deletions build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ if ((Get-ChildItem "$PSScriptRoot\packages" -Recurse).Count -eq 0) {
}

# Build
Copy-Item "$PSScriptRoot\packages\Newtonsoft.Json\lib\net45\Newtonsoft.Json.dll", "$PSScriptRoot\packages\Newtonsoft.Json.Schema\lib\net45\Newtonsoft.Json.Schema.dll" $build
Copy-Item "$PSScriptRoot\packages\Newtonsoft.Json\lib\net45\Newtonsoft.Json.dll", "$PSScriptRoot\packages\Newtonsoft.Json.Schema\lib\net45\Newtonsoft.Json.Schema.dll", "$PSscriptRoot\packages\YamlDotNet\lib\net45\YamlDotNet.dll" $build

Write-Output 'Compiling Scoop.Validator.cs ...'
$ScoopValidatorCs = @(
Expand All @@ -23,11 +23,12 @@ $ScoopValidatorCs = @(
'/optimize'
'/platform:anycpu'
'/target:library'
"/reference:""$build\Newtonsoft.Json.dll"",""$build\Newtonsoft.Json.Schema.dll"""
"/reference:""$build\Newtonsoft.Json.dll"",""$build\Newtonsoft.Json.Schema.dll"",""$build\YamlDotNet.dll"""
"/out:""$build\Scoop.Validator.dll"""
"$src\Scoop.Validator.cs"
)
& $csc @ScoopValidatorCs
if ($LASTEXITCODE -gt 0) { exit 1 }

Write-Output 'Compiling validator.cs ...'
$ValidatorCs = @(
Expand All @@ -41,9 +42,11 @@ $ValidatorCs = @(
"$src\validator.cs"
)
& $csc @ValidatorCs
if ($LASTEXITCODE -gt 0) { exit 1 }

# Package
7z a "$dist\validator.zip" "$build\*"
if ($LASTEXITCODE -gt 0) { exit 1 }
Get-ChildItem "$dist\*" | ForEach-Object {
$checksum = (Get-FileHash -Path $_.FullName -Algorithm 'SHA256').Hash.ToLower()
"$checksum *$($_.Name)" | Tee-Object -FilePath "$dist\$($_.Name).sha256" -Append
Expand Down
3 changes: 2 additions & 1 deletion packages.config
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Net.Compilers" version="3.0.0" targetFramework="net45" developmentDependency="true" />
<package id="Newtonsoft.Json" version="12.0.2" targetFramework="net45" />
<package id="Newtonsoft.Json.Schema" version="3.0.11" targetFramework="net45" />
<package id="Microsoft.Net.Compilers" version="3.0.0" targetFramework="net45" developmentDependency="true" />
<package id="YamlDotNet" version="5.3.0" targetFramework="net45" />
</packages>
68 changes: 68 additions & 0 deletions src/Scoop.Validator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Schema;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;

namespace Scoop {
public class JsonParserException : Exception {
Expand Down Expand Up @@ -37,12 +39,35 @@ private JSchema ParseSchema(string file) {
}

private JObject ParseManifest(string file) {
var isYaml = false;
if (file.EndsWith(".yml") || file.EndsWith(".yaml")) {
isYaml = true;
object yamlObject;
using (var r = new StreamReader(file)) {
yamlObject = new DeserializerBuilder()
.WithNodeTypeResolver(new InferTypeFromValue())
.Build()
.Deserialize(r);
}

var js = new SerializerBuilder()
.JsonCompatible()
.Build();

file = $"{file}-{new Random().Next(258, 258258)}.json";
using (StreamWriter writer = new StreamWriter(file)) {
js.Serialize(writer, yamlObject);
}
}

try {
return JObject.Parse(File.ReadAllText(file, System.Text.Encoding.UTF8));
} catch (Newtonsoft.Json.JsonReaderException e) {
throw new JsonParserException(Path.GetFileName(file), e.Message, e);
} catch (FileNotFoundException e) {
throw e;
} finally {
if (isYaml) { File.Delete(file); }
}
}

Expand Down Expand Up @@ -121,4 +146,47 @@ public void traverseErrors(IList<ValidationError> errors, int level = 1) {
}
}
}

// Helper class for workaround of boolean handling
public class InferTypeFromValue : INodeTypeResolver {
public bool Resolve(NodeEvent nodeEvent, ref Type currentType) {
// https://yaml.org/type/bool.html
string[] yamlBool = {
"y",
"Y",
"yes",
"Yes",
"YES",

"n",
"N",
"no",
"No",
"NO",

"true",
"True",
"TRUE",
"false",
"False",
"FALSE",
"on",
"On",
"ON",
"off",
"Off",
"OFF"
};
var scalar = nodeEvent as Scalar;

if (scalar != null) {
if (Array.Exists(yamlBool, (v) => { return v == scalar.Value; })) {
currentType = typeof(bool);
return true;
}
}

return false;
}
}
}
31 changes: 17 additions & 14 deletions src/validator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@
namespace Scoop {
public class Program {
public static int Main(string[] args) {
bool ci = String.Format("{0}", Environment.GetEnvironmentVariable("CI")).ToLower() == "true";
bool valid = true;

if (args.Length < 2) {
Console.WriteLine("Usage: validator.exe <schema> <manifest> [<manifest>...]");
Console.WriteLine("Usage: validator.exe <SCHEMA> <MANIFEST>...");
return 1;
}

bool ci = String.Format("{0}", Environment.GetEnvironmentVariable("CI")).ToLower() == "true";
bool valid = true;

IList<string> manifests = args.ToList<String>();
String schema = manifests.First();
manifests.RemoveAt(0);
String combinedArgs = String.Join("", manifests);

if (combinedArgs.Contains("*") || combinedArgs.Contains("?")) {
try {
var path = new Uri(Path.Combine(Directory.GetCurrentDirectory(), combinedArgs)).LocalPath;
Expand All @@ -33,20 +33,23 @@ public static int Main(string[] args) {
}

Scoop.Validator validator = new Scoop.Validator(schema, ci);

foreach (var manifest in manifests) {
if (validator.Validate(manifest)) {
if (ci) {
Console.WriteLine(" [+] {0} validates against the schema!", Path.GetFileName(manifest));
} else {
Console.WriteLine("- {0} validates against the schema!", Path.GetFileName(manifest));
}
var prefix = ci ? " [+]" : "-";
Console.WriteLine("{0} {1} validates against the schema!", prefix, Path.GetFileName(manifest));
} else {
if (ci) {
Console.WriteLine(" [-] {0} has {1} Error{2}!", Path.GetFileName(manifest), validator.Errors.Count, validator.Errors.Count > 1 ? "s" : "");
} else {
Console.WriteLine("- {0} has {1} Error{2}!", Path.GetFileName(manifest), validator.Errors.Count, validator.Errors.Count > 1 ? "s" : "");
}
var prefix = ci ? " [-]" : "-";
Console.WriteLine(
"{0} {1} has {2} Error{3}",
prefix,
Path.GetFileName(manifest),
validator.Errors.Count,
validator.Errors.Count > 1 ? "s" : ""
);

valid = false;

foreach (var error in validator.Errors) {
Console.WriteLine(error);
}
Expand Down