diff --git a/src/CodeQLToolkit.Features.Pack/CodeQLToolkit.Features.Pack.csproj b/src/CodeQLToolkit.Features.Pack/CodeQLToolkit.Features.Pack.csproj index 0fdbb7f..8afb4a9 100644 --- a/src/CodeQLToolkit.Features.Pack/CodeQLToolkit.Features.Pack.csproj +++ b/src/CodeQLToolkit.Features.Pack/CodeQLToolkit.Features.Pack.csproj @@ -15,4 +15,9 @@ + + + + + diff --git a/src/CodeQLToolkit.Features.Pack/Commands/PackCommandFeature.cs b/src/CodeQLToolkit.Features.Pack/Commands/PackCommandFeature.cs deleted file mode 100644 index d01968b..0000000 --- a/src/CodeQLToolkit.Features.Pack/Commands/PackCommandFeature.cs +++ /dev/null @@ -1,63 +0,0 @@ -using CodeQLToolkit.Features.Pack.Commands.Targets; -using System; -using System.Collections.Generic; -using System.CommandLine; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace CodeQLToolkit.Features.Pack.Commands -{ - public class PackCommandFeature : FeatureBase, IToolkitCommandFeature - { - public void Register(Command parentCommand) - { - Log.G().LogInformation("Registering command submodule."); - - var runCommand = new Command("run", "Functions pertaining to running pack-related commands."); - parentCommand.Add(runCommand); - - // a command that installs query packs - var sayHello = new Command("hello-jeongsoo", "Says hello!"); - var howManyTimesHello = new Option("--times", "how many times to say it") { IsRequired = true }; - sayHello.Add(howManyTimesHello); - - var sayGoodbye = new Command("goodbye-jeongsoo", "Says goodbye!"); - - var howManyTimes = new Option("--times", "how many times to say it") { IsRequired = true }; - sayGoodbye.Add(howManyTimes); - - - runCommand.Add(sayHello); - runCommand.Add(sayGoodbye); - - sayHello.SetHandler((basePath, times) => { - - new HelloJeongsooCommandTarget() { - Base = basePath, - Times = times - - }.Run(); - - }, Globals.BasePathOption, howManyTimesHello); - - sayGoodbye.SetHandler((basePath, times) => { - - Console.WriteLine($"Saying goodbye {times} number of times"); - - for (int i = 0; i < times; i++) - { - Console.WriteLine("Goodbye!"); - } - - - }, Globals.BasePathOption, howManyTimes); - - } - - public int Run() - { - throw new NotImplementedException(); - } - } -} diff --git a/src/CodeQLToolkit.Features.Pack/Commands/Targets/HelloJeongsooCommandTarget.cs b/src/CodeQLToolkit.Features.Pack/Commands/Targets/HelloJeongsooCommandTarget.cs deleted file mode 100644 index 33b4420..0000000 --- a/src/CodeQLToolkit.Features.Pack/Commands/Targets/HelloJeongsooCommandTarget.cs +++ /dev/null @@ -1,45 +0,0 @@ -using CodeQLToolkit.Shared.Utils; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace CodeQLToolkit.Features.Pack.Commands.Targets -{ - public class HelloJeongsooCommandTarget : CommandTarget - { - - public int Times { get; set; } - - - public override void Run() - { - for(int i = 0; i < Times; i++) { - Console.WriteLine($"Hello! My Base Target is: {Base}"); - } - - - var c = new QLTConfig() - { - Base = Base - }; - - if (!File.Exists(c.CodeQLConfigFilePath)) - { - ProcessUtils.DieWithError($"Cannot read values from missing file {c.CodeQLConfigFilePath}"); - } - - var config = c.FromFile(); - - - Console.WriteLine($"---------current settings---------"); - Console.WriteLine($"CodeQL CLI Version: {config.CodeQLCLI}"); - Console.WriteLine($"CodeQL Standard Library Version: {config.CodeQLStandardLibrary}"); - Console.WriteLine($"CodeQL CLI Bundle Version: {config.CodeQLCLIBundle}"); - Console.WriteLine($"----------------------------------"); - Console.WriteLine("(hint: use `qlt codeql set` to modify these values.)"); - - } - } -} diff --git a/src/CodeQLToolkit.Features.Pack/Commands/Validate/Targets/Schemas/ExtQlpackYmlSchema.cs b/src/CodeQLToolkit.Features.Pack/Commands/Validate/Targets/Schemas/ExtQlpackYmlSchema.cs new file mode 100644 index 0000000..6cdb3ad --- /dev/null +++ b/src/CodeQLToolkit.Features.Pack/Commands/Validate/Targets/Schemas/ExtQlpackYmlSchema.cs @@ -0,0 +1,31 @@ +namespace CodeQLToolkit.Features.Pack.Commands.Validate.Schemas +{ + public class ExtQlpackYmlFileSchema : IYmlSchema + { + public bool Library { get; set; } + public string Name { get; set; } + public string Version { get; set; } + public Dictionary ExtensionTargets { get; set; } + public List DataExtensions { get; set; } + + override public string ToString() + { + string ExtensionTargetsString = "\n"; + if (ExtensionTargets is not null) + foreach (KeyValuePair pair in ExtensionTargets) + ExtensionTargetsString += $"{pair.Key}: {pair.Value}\n"; + + string DataExtensionString = "\n"; + if (DataExtensions is not null) + foreach (string DataExtension in DataExtensions) + DataExtensionString += $"{DataExtension}, "; + + return $@"Ext qlpack.yml file: + Library: {Library}, + Name: {Name}, + Version: {Version}, + ExtensionTargets: {ExtensionTargetsString} + DataExtensions: {DataExtensionString}"; + } + } +} diff --git a/src/CodeQLToolkit.Features.Pack/Commands/Validate/Targets/Schemas/IYmlSchema.cs b/src/CodeQLToolkit.Features.Pack/Commands/Validate/Targets/Schemas/IYmlSchema.cs new file mode 100644 index 0000000..88ba606 --- /dev/null +++ b/src/CodeQLToolkit.Features.Pack/Commands/Validate/Targets/Schemas/IYmlSchema.cs @@ -0,0 +1,6 @@ +namespace CodeQLToolkit.Features.Pack.Commands.Validate.Schemas { + public interface IYmlSchema { + public string Name { get; set; } + public string Version { get; set; } + } +} \ No newline at end of file diff --git a/src/CodeQLToolkit.Features.Pack/Commands/Validate/Targets/Schemas/LibQlpackYmlSchema.cs b/src/CodeQLToolkit.Features.Pack/Commands/Validate/Targets/Schemas/LibQlpackYmlSchema.cs new file mode 100644 index 0000000..cf765fa --- /dev/null +++ b/src/CodeQLToolkit.Features.Pack/Commands/Validate/Targets/Schemas/LibQlpackYmlSchema.cs @@ -0,0 +1,28 @@ +namespace CodeQLToolkit.Features.Pack.Commands.Validate.Schemas +{ + public class LibQlpackYmlFileSchema : IYmlSchema + { + public bool Library { get; set; } + public string Name { get; set; } + public string Version { get; set; } + public string Suites { get; set; } + public string Extractor { get; set; } + public Dictionary Dependencies { get; set; } + + override public string ToString() + { + string DependenciesString = "\n"; + if (Dependencies is not null) + foreach (KeyValuePair pair in Dependencies) + DependenciesString += $"{pair.Key}: {pair.Value}\n"; + + return $@"Lib qlpack.yml file: + Library: {Library}, + Name: {Name}, + Version: {Version}, + Suites: {Suites}, + Extractor: {Extractor}, + Dependencies: {DependenciesString}"; + } + } +} diff --git a/src/CodeQLToolkit.Features.Pack/Commands/Validate/Targets/Schemas/SrcQlpackYmlSchema.cs b/src/CodeQLToolkit.Features.Pack/Commands/Validate/Targets/Schemas/SrcQlpackYmlSchema.cs new file mode 100644 index 0000000..aa1c4cc --- /dev/null +++ b/src/CodeQLToolkit.Features.Pack/Commands/Validate/Targets/Schemas/SrcQlpackYmlSchema.cs @@ -0,0 +1,35 @@ +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace CodeQLToolkit.Features.Pack.Commands.Validate.Schemas +{ + public class SrcQlpackYmlSchema : IYmlSchema + { + public bool Library { get; set; } + public string Name { get; set; } + public string Version { get; set; } + public string Suites { get; set; } + public string Extractor { get; set; } + public Dictionary Dependencies { get; set; } + + [YamlMember(Alias = "default-suite-file", ApplyNamingConventions = false)] + public string DefaultSuiteFile { get; set; } + + public override string ToString() + { + string DependenciesString = "\n"; + if (Dependencies is not null) + foreach (KeyValuePair pair in Dependencies) + DependenciesString += $"{pair.Key}: {pair.Value}\n"; + + return $@"Src qlpack.yml file: + Library: {Library}, + Name: {Name}, + Version: {Version}, + Suites: {Suites}, + Extractor: {Extractor}, + Dependencies: {DependenciesString}, + DefaultSuiteFile: {DefaultSuiteFile}"; + } + } +} \ No newline at end of file diff --git a/src/CodeQLToolkit.Features.Pack/Commands/Validate/Targets/Schemas/TestQlpackYmlSchema.cs b/src/CodeQLToolkit.Features.Pack/Commands/Validate/Targets/Schemas/TestQlpackYmlSchema.cs new file mode 100644 index 0000000..35df92b --- /dev/null +++ b/src/CodeQLToolkit.Features.Pack/Commands/Validate/Targets/Schemas/TestQlpackYmlSchema.cs @@ -0,0 +1,24 @@ +namespace CodeQLToolkit.Features.Pack.Commands.Validate.Schemas +{ + public class TestQlpackYmlFileSchema : IYmlSchema + { + public string Name { get; set; } + public string Version { get; set; } + public string Extractor { get; set; } + public Dictionary Dependencies { get; set; } + + override public string ToString() + { + string DependenciesString = "\n"; + if (Dependencies is not null) + foreach (KeyValuePair pair in Dependencies) + DependenciesString += $"{pair.Key}: {pair.Value}\n"; + + return $@"Test qlpack.yml file: + Name: {Name}, + Version: {Version}, + Extractor: {Extractor}, + Dependencies: {DependenciesString}"; + } + } +} \ No newline at end of file diff --git a/src/CodeQLToolkit.Features.Pack/Commands/Validate/Targets/ValidateSyntaxTarget.cs b/src/CodeQLToolkit.Features.Pack/Commands/Validate/Targets/ValidateSyntaxTarget.cs new file mode 100644 index 0000000..64b9c6f --- /dev/null +++ b/src/CodeQLToolkit.Features.Pack/Commands/Validate/Targets/ValidateSyntaxTarget.cs @@ -0,0 +1,16 @@ +using CodeQLToolkit.Shared.Logging; +using CodeQLToolkit.Shared.Utils; +using YamlDotNet.Serialization; + +namespace CodeQLToolkit.Features.Pack.Commands.Validate.Targets +{ + public class ValidateSyntaxTarget : CommandTarget + { + public string ExtFilePath { get; set; } + + public override void Run() + { + Console.WriteLine("Hello world!"); + } + } +} \ No newline at end of file diff --git a/src/CodeQLToolkit.Features.Pack/Commands/Validate/Targets/ValidateVersionTarget.cs b/src/CodeQLToolkit.Features.Pack/Commands/Validate/Targets/ValidateVersionTarget.cs new file mode 100644 index 0000000..54a826b --- /dev/null +++ b/src/CodeQLToolkit.Features.Pack/Commands/Validate/Targets/ValidateVersionTarget.cs @@ -0,0 +1,96 @@ +using CodeQLToolkit.Shared.Logging; +using CodeQLToolkit.Shared.Utils; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; +using Semver; +using CodeQLToolkit.Features.Pack.Commands.Validate.Schemas; +using System.Diagnostics; + +namespace CodeQLToolkit.Features.Pack.Commands.Validate.Targets +{ + public class YamlParseException : Exception + { + public string message { get; set; } + public YamlParseException(string failMessage) + { + message = failMessage; + } + } + public class ValidateVersionTarget : CommandTarget + { + public string[] QlpackYmlFiles { get; set; } + private static Deserializer YamlDeserializer = (Deserializer)new DeserializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).Build(); + + public override void Run() + { + Console.WriteLine("Hello world! I got: "); + foreach (string qlpackYmlFile in QlpackYmlFiles) + private static UnbrokenSemVersionRange FindUnbrokenSemverRangeOverlap( + UnbrokenSemVersionRange range1, + UnbrokenSemVersionRange range2 + ) + { + if (range1.End.ComparePrecedenceTo(range2.Start) == -1 || range2.End.ComparePrecedenceTo(range1.Start) == -1) { + return UnbrokenSemVersionRange.Empty; + } else { + var start = range1.Start.ComparePrecedenceTo(range2.Start) == -1 ? range2.Start : range1.Start; + var end = range1.End.ComparePrecedenceTo(range2.End) == -1 ? range1.End : range2.End; + return UnbrokenSemVersionRange.Inclusive(start, end); + } + } + + private static SemVersionRange FindSemverRangeOverlap( + SemVersionRange range1, + SemVersionRange range2 + ) + { + var acc = new List(); + foreach (UnbrokenSemVersionRange unbrokenRange1 in range1) + foreach (UnbrokenSemVersionRange unbrokenRange2 in range2) + acc.Add(FindUnbrokenSemverRangeOverlap(unbrokenRange1, unbrokenRange2)); + + return SemVersionRange.Create(acc); + } + + private static bool thereIsSemVersionRangeOverlap( + SemVersionRange range1, + SemVersionRange range2 + ) + { + var intersection = FindSemverRangeOverlap(range1, range2); + bool foundNonemptyUnbrokenSemVersionRange = false; + + foreach (UnbrokenSemVersionRange unbrokenRange in intersection) + { + Console.WriteLine($"Inspecting [{unbrokenRange.Start}, {unbrokenRange.End})..."); + if (!unbrokenRange.Equals(UnbrokenSemVersionRange.Empty)) + foundNonemptyUnbrokenSemVersionRange = true; + break; + } + + return foundNonemptyUnbrokenSemVersionRange; + } + + private static void testRangeOverlap() + { + var semver21 = SemVersionRange.Parse("1.2.1"); + var semver22 = SemVersionRange.Parse("^1.2.0"); + + Debug.Assert(thereIsSemVersionRangeOverlap(semver21, semver22)); + + var semver31 = SemVersionRange.Parse("^1.2.0"); + var semver32 = SemVersionRange.Parse("1.2.1"); + + Debug.Assert(thereIsSemVersionRangeOverlap(semver31, semver32)); + + var semver41 = UnbrokenSemVersionRange.AtLeast(SemVersion.Parse("1.2.0")); + var semver42 = UnbrokenSemVersionRange.Equals(SemVersion.Parse("1.2.1")); + + Console.WriteLine($"semver41: {semver41.Start}, {semver41.End}"); + Console.WriteLine($"semver42: {semver42.Start}, {semver42.End}"); + + var intersection = FindUnbrokenSemverRangeOverlap(semver41, semver42); + Console.WriteLine($"intersection: {intersection.Start}, {intersection.End}"); + } + } +} diff --git a/src/CodeQLToolkit.Features.Pack/Commands/Validate/ValidateFeature.cs b/src/CodeQLToolkit.Features.Pack/Commands/Validate/ValidateFeature.cs new file mode 100644 index 0000000..2760765 --- /dev/null +++ b/src/CodeQLToolkit.Features.Pack/Commands/Validate/ValidateFeature.cs @@ -0,0 +1,70 @@ +using CodeQLToolkit.Shared.Logging; +using CodeQLToolkit.Shared.Utils; +using CodeQLToolkit.Shared.Feature; +using CodeQLToolkit.Features.Pack.Commands.Validate.Targets; +using System.CommandLine; +using System.IO; + +namespace CodeQLToolkit.Features.Pack.Commands.Validate +{ + public class ValidateFeature : FeatureBase, IToolkitCommandFeature + { + public void Register(Command parentCommand) + { + Log.G().LogInformation("Registering Validate submodule."); + var validateCommand = new Command("validate", "Checking correctness of various files"); + parentCommand.Add(validateCommand); + + var validateSyntaxCommand = new Command("syntax", "Check if the extensions designators are in correct syntax"); + var extFilePath = new Option("--ext", "path to the extensions' definition file in yml format") { IsRequired = true, Arity = ArgumentArity.ExactlyOne }; + validateSyntaxCommand.Add(extFilePath); + + var validateVersionCommand = new Command("version", "Check if the versions match up across the qlpack.yml files"); + var qlpackYmlFiles = new Option("--yml", "qlpack.yml files to check against") { IsRequired = true, Arity = ArgumentArity.OneOrMore, AllowMultipleArgumentsPerToken = true }; + validateVersionCommand.Add(qlpackYmlFiles); + + validateCommand.Add(validateSyntaxCommand); + validateCommand.Add(validateVersionCommand); + + validateSyntaxCommand.SetHandler((basePath, extFilePath) => + { + var fileExists = File.Exists(extFilePath); + var isYamlFile = Path.GetExtension(extFilePath) == ".yml" || Path.GetExtension(extFilePath) == ".yaml"; + if (!fileExists) + DieWithError($"{extFilePath} does not exist."); + if (!isYamlFile) + DieWithError($"{extFilePath} is not a yaml file."); + new ValidateSyntaxTarget() + { + Base = basePath, + ExtFilePath = extFilePath + }.Run(); + }, Globals.BasePathOption, extFilePath); + validateVersionCommand.SetHandler(() => { throw new NotImplementedException(); }); + + validateVersionCommand.SetHandler((basePath, qlpackYmlFiles) => + { + foreach (var qlpackYmlFile in qlpackYmlFiles) + { + var fileExists = File.Exists(qlpackYmlFile); + var isYamlFile = Path.GetExtension(qlpackYmlFile) == ".yml" || Path.GetExtension(qlpackYmlFile) == ".yaml"; + if (!fileExists) + DieWithError($"{qlpackYmlFile} does not exist."); + if (!isYamlFile) + DieWithError($"{qlpackYmlFile} is not a yaml file."); + } + new ValidateVersionTarget() + { + Base = basePath, + QlpackYmlFiles = qlpackYmlFiles + }.Run(); + }, Globals.BasePathOption, qlpackYmlFiles + ); + } + public int Run() + { + throw new NotImplementedException(); + } + } + +} diff --git a/src/CodeQLToolkit.Features.Pack/PackFeatureMain.cs b/src/CodeQLToolkit.Features.Pack/PackFeatureMain.cs index 9fa81ea..f0d1ad6 100644 --- a/src/CodeQLToolkit.Features.Pack/PackFeatureMain.cs +++ b/src/CodeQLToolkit.Features.Pack/PackFeatureMain.cs @@ -1,4 +1,5 @@ using CodeQLToolkit.Features.Pack.Commands; +using CodeQLToolkit.Features.Pack.Commands.Validate; using System; using System.Collections.Generic; using System.CommandLine; @@ -12,7 +13,7 @@ public class PackFeatureMain : IToolkitFeature { readonly static PackFeatureMain instance; - readonly PackCommandFeature commandFeature; + readonly ValidateFeature validateFeature; static PackFeatureMain() { @@ -21,7 +22,7 @@ static PackFeatureMain() private PackFeatureMain() { - commandFeature = new PackCommandFeature(); + validateFeature = new ValidateFeature(); } public static PackFeatureMain Instance { get { return instance; } } @@ -31,7 +32,7 @@ public void Register(Command parentCommand) parentCommand.Add(packCommand); Log.G().LogInformation("Registering scaffolding submodule."); - commandFeature.Register(packCommand); + validateFeature.Register(packCommand); } public int Run()