Skip to content

Commit 1feda7a

Browse files
authored
[WIP] Validate manifests against JSON Schema in CI Tests (#1331)
* Add suggest and psmodule to schema.json * Fix required fields in schema.json * Improve url validation in schema.json * Add validator.exe as a single file validation tool * Add Scoop.Validator Lib for use in Manifest-Tests * Add buildscript for Scoop.Validator and validator.exe * Exclude .dll and packages folder from Project-Tests * Validate manifests against JSON Schema in CI Tests * Complete JSON Schema Validation * Dlls shouldn't be treated as text
1 parent 02238f0 commit 1feda7a

17 files changed

+348
-39
lines changed

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
* text eol=crlf
33
*.exe -text
44
*.zip -text
5+
*.dll -text

schema.json

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -100,14 +100,23 @@
100100
},
101101
"type": {
102102
"enum": [
103+
"md5",
103104
"sha1",
104105
"sha256",
105106
"sha512"
106107
]
107108
},
108109
"url": {
109-
"format": "uri",
110-
"type": "string"
110+
"anyOf": [
111+
{
112+
"format": "uri",
113+
"type": "string"
114+
},
115+
{
116+
"pattern": "^\\$url.[\\w\\d]+$",
117+
"type": "string"
118+
}
119+
]
111120
}
112121
},
113122
"type": "object"
@@ -190,8 +199,7 @@
190199
"type": "string"
191200
},
192201
"minItems": 1,
193-
"type": "array",
194-
"uniqueItems": true
202+
"type": "array"
195203
}
196204
]
197205
},
@@ -236,16 +244,21 @@
236244
"anyOf": [
237245
{
238246
"format": "uri",
239-
"type": "string"
247+
"type": "string",
248+
"not": {
249+
"pattern": "(\\$)"
250+
}
240251
},
241252
{
242253
"items": {
243254
"format": "uri",
244-
"type": "string"
255+
"type": "string",
256+
"not": {
257+
"pattern": "(\\$)"
258+
}
245259
},
246260
"minItems": 1,
247-
"type": "array",
248-
"uniqueItems": true
261+
"type": "array"
249262
}
250263
]
251264
}
@@ -413,8 +426,29 @@
413426
},
414427
"version": {
415428
"type": "string"
429+
},
430+
"suggest": {
431+
"additionalProperties": false,
432+
"patternProperties": {
433+
"^(.*)$": {
434+
"$ref": "#/definitions/stringOrArrayOfStrings"
435+
}
436+
},
437+
"type": "object"
438+
},
439+
"psmodule": {
440+
"additionalProperties": false,
441+
"properties": {
442+
"name": {
443+
"type": "string"
444+
}
445+
},
446+
"type": "object"
416447
}
417448
},
418449
"title": "scoop app manifest schema",
419-
"type": "object"
450+
"type": "object",
451+
"required": [
452+
"version"
453+
]
420454
}

supporting/validator/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
packages/
Binary file not shown.
478 KB
Binary file not shown.
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using Newtonsoft.Json;
5+
using Newtonsoft.Json.Linq;
6+
using Newtonsoft.Json.Schema;
7+
8+
namespace Scoop
9+
{
10+
public class JsonParserException : Exception
11+
{
12+
public string FileName { get; set; }
13+
public JsonParserException(string file, string message) : base(message) { this.FileName = file; }
14+
public JsonParserException(string file, string message, Exception inner) : base(message, inner) { this.FileName = file; }
15+
}
16+
17+
public class Validator
18+
{
19+
private bool CI { get; set; }
20+
public JSchema Schema { get; private set; }
21+
public FileInfo SchemaFile { get; private set; }
22+
public JObject Manifest { get; private set; }
23+
public FileInfo ManifestFile { get; private set; }
24+
public IList<string> Errors { get; private set; }
25+
public string ErrorsAsString
26+
{
27+
get
28+
{
29+
return String.Join(System.Environment.NewLine, this.Errors);
30+
}
31+
}
32+
33+
private JSchema ParseSchema(string file)
34+
{
35+
try
36+
{
37+
return JSchema.Parse(File.ReadAllText(file, System.Text.Encoding.UTF8));
38+
}
39+
catch (Newtonsoft.Json.JsonReaderException e)
40+
{
41+
throw new JsonParserException(Path.GetFileName(file), e.Message, e);
42+
}
43+
catch (FileNotFoundException e)
44+
{
45+
throw e;
46+
}
47+
}
48+
49+
private JObject ParseManifest(string file)
50+
{
51+
try
52+
{
53+
return JObject.Parse(File.ReadAllText(file, System.Text.Encoding.UTF8));
54+
}
55+
catch (Newtonsoft.Json.JsonReaderException e)
56+
{
57+
throw new JsonParserException(Path.GetFileName(file), e.Message, e);
58+
}
59+
catch (FileNotFoundException e)
60+
{
61+
throw e;
62+
}
63+
}
64+
65+
public Validator(string schemaFile)
66+
{
67+
this.SchemaFile = new FileInfo(schemaFile);
68+
this.Errors = new List<string>();
69+
}
70+
71+
public Validator(string schemaFile, bool ci)
72+
{
73+
this.SchemaFile = new FileInfo(schemaFile);
74+
this.Errors = new List<string>();
75+
this.CI = ci;
76+
}
77+
78+
public bool Validate(string file)
79+
{
80+
this.ManifestFile = new FileInfo(file);
81+
return this.Validate();
82+
}
83+
84+
public bool Validate()
85+
{
86+
this.Errors.Clear();
87+
try
88+
{
89+
if (this.Schema == null)
90+
{
91+
this.Schema = this.ParseSchema(this.SchemaFile.FullName);
92+
}
93+
this.Manifest = this.ParseManifest(this.ManifestFile.FullName);
94+
}
95+
catch (FileNotFoundException e)
96+
{
97+
this.Errors.Add(e.Message);
98+
}
99+
catch (JsonParserException e)
100+
{
101+
this.Errors.Add(String.Format("{0}{1}: {2}", (this.CI ? " [*] " : ""), e.FileName, e.Message));
102+
}
103+
104+
if (this.Schema == null || this.Manifest == null)
105+
return false;
106+
107+
IList<ValidationError> validationErrors = new List<ValidationError>();
108+
109+
this.Manifest.IsValid(this.Schema, out validationErrors);
110+
111+
if (validationErrors.Count > 0)
112+
{
113+
foreach (ValidationError error in validationErrors)
114+
{
115+
this.Errors.Add(String.Format("{0}{1}: {2}", (this.CI ? " [*] " : ""), this.ManifestFile.Name, error.Message));
116+
foreach (ValidationError childError in error.ChildErrors)
117+
{
118+
this.Errors.Add(String.Format((this.CI ? " [^] {0}{1}" : "{0}^ {1}"), new String(' ', this.ManifestFile.Name.Length + 2), childError.Message));
119+
}
120+
}
121+
}
122+
123+
return (this.Errors.Count == 0);
124+
}
125+
}
126+
}
6.5 KB
Binary file not shown.

supporting/validator/build.ps1

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
$fwdir = gci C:\Windows\Microsoft.NET\Framework\ -dir | sort -desc | select -first 1
2+
3+
pushd $psscriptroot
4+
& nuget restore -solutiondirectory .
5+
gci $psscriptroot\packages\Newtonsoft.*\lib\net40\*.dll -file | % { copy-item $_ $psscriptroot }
6+
& "$($fwdir.fullname)\csc.exe" /platform:anycpu /nologo /optimize /target:library /reference:Newtonsoft.Json.dll,Newtonsoft.Json.Schema.dll Scoop.Validator.cs
7+
& "$($fwdir.fullname)\csc.exe" /platform:anycpu /nologo /optimize /target:exe /reference:Scoop.Validator.dll,Newtonsoft.Json.dll,Newtonsoft.Json.Schema.dll validator.cs
8+
popd

supporting/validator/packages.config

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<packages>
3+
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
4+
<package id="Newtonsoft.Json.Schema" version="2.0.8" targetFramework="net45" />
5+
</packages>

supporting/validator/validator.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using System;
2+
using System.IO;
3+
4+
namespace Scoop
5+
{
6+
public class Program
7+
{
8+
public static int Main(string[] args)
9+
{
10+
bool ci = (args.Length == 3 && args[2] == "-ci");
11+
bool valid = false;
12+
13+
if (args.Length < 2)
14+
{
15+
Console.WriteLine("Usage: validator.exe schema.json manifest.json");
16+
return 1;
17+
}
18+
19+
Scoop.Validator validator = new Scoop.Validator(args[0], ci);
20+
valid = validator.Validate(args[1]);
21+
22+
if (valid)
23+
{
24+
Console.WriteLine("Yay! {0} validates against the schema!", Path.GetFileName(args[1]));
25+
}
26+
else
27+
{
28+
foreach (var error in validator.Errors)
29+
{
30+
Console.WriteLine(error);
31+
}
32+
}
33+
34+
return valid ? 0 : 1;
35+
}
36+
}
37+
}

0 commit comments

Comments
 (0)