diff --git a/src/CodeQLToolkit.Features/CodeQLToolkit.Features.csproj b/src/CodeQLToolkit.Features/CodeQLToolkit.Features.csproj
index e458176..269a128 100644
--- a/src/CodeQLToolkit.Features/CodeQLToolkit.Features.csproj
+++ b/src/CodeQLToolkit.Features/CodeQLToolkit.Features.csproj
@@ -12,9 +12,8 @@
-
-
+
@@ -72,6 +71,9 @@
Always
+
+ Always
+
diff --git a/src/CodeQLToolkit.Features/Templates/Validation/Actions/validate-query-metadata.liquid b/src/CodeQLToolkit.Features/Templates/Validation/Actions/validate-query-metadata.liquid
new file mode 100644
index 0000000..97e9a09
--- /dev/null
+++ b/src/CodeQLToolkit.Features/Templates/Validation/Actions/validate-query-metadata.liquid
@@ -0,0 +1,86 @@
+name: ⚙️ CodeQL - Validate Queries ({{language}})
+{% raw %}
+on:
+ push:
+ branches:
+ - '**'
+ pull_request:
+ branches:
+ - '**'
+ workflow_dispatch:
+
+jobs:
+ create-matrix:
+ name: Create CodeQL Test Matrix
+ runs-on: ubuntu-latest
+ outputs:
+ matrix: ${{ steps.export-test-matrix.outputs.matrix }}
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+
+ - name: Install QLT
+ id: install-qlt
+ uses: ./.github/actions/install-qlt
+ with:
+ qlt-version: 'latest'
+ add-to-path: true
+{% endraw %}
+ - name: Export test matrix
+ id: export-test-matrix
+ run: |
+ qlt test run get-matrix --os-version {{ use_runner }}
+{% raw %}
+ validate-queries:
+ name: Validate Queries
+ needs: create-matrix
+
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix: ${{ fromJSON(needs.create-matrix.outputs.matrix) }}
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+
+ - name: Install QLT
+ uses: ./.github/actions/install-qlt
+ with:
+ qlt-version: 'latest'
+ add-to-path: true
+
+ - name: Install CodeQL
+ uses: ./.github/actions/install-codeql
+ with:
+ codeql-cli-version: ${{ matrix.codeql_cli }}
+ codeql-stdlib-version: ${{ matrix.codeql_standard_library }}
+ add-to-path: true
+
+ - name: Verify Versions of Tooling
+ shell: bash
+ run: |
+ echo "CodeQL Home: ${{ steps.install-codeql.outputs.codeql-home }}"
+ echo -e "Checking CodeQL Version:"
+ codeql --version
+
+ echo -e "Checking QLT Version:"
+ echo "QLT Home: ${{ steps.install-qlt.outputs.qlt-home }}"
+ qlt version
+
+ - name: Install QL Packs
+ shell: bash
+ run: |
+ qlt query run install-packs
+
+ - name: Run validation tests
+ shell: bash
+ run: >
+{% endraw %}
+ # run a copy for pretty printing
+ qlt validation run check-queries --pretty-print
+ --language {{ language }}
+
+ # run this version to influence the outcome of the run.
+ qlt validation run check-queries
+ --language {{ language }}
diff --git a/src/CodeQLToolkit.Features/Test/Lifecycle/Targets/Actions/InitLifecycleTarget.cs b/src/CodeQLToolkit.Features/Test/Lifecycle/Targets/Actions/InitLifecycleTarget.cs
index b505101..b300412 100644
--- a/src/CodeQLToolkit.Features/Test/Lifecycle/Targets/Actions/InitLifecycleTarget.cs
+++ b/src/CodeQLToolkit.Features/Test/Lifecycle/Targets/Actions/InitLifecycleTarget.cs
@@ -55,6 +55,8 @@ runner with the `--use-runner` argument.
(Hint: If you'd like to regenerate your files, you can use the `--overwrite-existing` option to overwrite the files that are in place now.)";
+ Log.G().LogInformation(message);
+
}
}
diff --git a/src/CodeQLToolkit.Features/Validation/Commands/Targets/CheckQueriesCommandTarget.cs b/src/CodeQLToolkit.Features/Validation/Commands/Targets/CheckQueriesCommandTarget.cs
new file mode 100644
index 0000000..cda273e
--- /dev/null
+++ b/src/CodeQLToolkit.Features/Validation/Commands/Targets/CheckQueriesCommandTarget.cs
@@ -0,0 +1,81 @@
+using CodeQLToolkit.Features.Query.Commands.Targets;
+using CodeQLToolkit.Features.Validation.Models;
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CodeQLToolkit.Features.Validation.Commands.Targets
+{
+ public class CheckQueriesCommandTarget : CommandTarget
+ {
+
+ public bool PrettyPrint { get; set; }
+
+
+ public override void Run()
+ {
+ Log.G().LogInformation($"Validating query metadata for {Language}...");
+
+ using (Process process = new Process())
+ {
+ process.StartInfo.FileName = "codeql";
+ process.StartInfo.UseShellExecute = false;
+ process.StartInfo.WorkingDirectory = Base;
+ process.StartInfo.RedirectStandardOutput = true;
+ process.StartInfo.RedirectStandardError = false;
+ process.StartInfo.Arguments = $"query compile --format json -n {Language}";
+ process.Start();
+
+ var output = process.StandardOutput.ReadToEnd();
+
+ process.WaitForExit();
+
+ if (process.ExitCode != 0)
+ {
+ DieWithError($"Fatal error. Please check error output.");
+ }
+
+ var results = JsonConvert.DeserializeObject>(output);
+
+ bool shouldFail = false;
+
+ if (results != null)
+ {
+ foreach (var r in results)
+ {
+ foreach (var message in r.messages)
+ {
+ if (message.severity == "WARNING" || message.severity == "ERROR")
+ {
+ shouldFail = true;
+
+ if (PrettyPrint)
+ {
+ Console.WriteLine($"❌ [{message.severity}] {message.message}: {message.position.fileName}:{message.position.line},{message.position.column}-{message.position.endColumn},{message.position.endColumn}");
+ }
+ else
+ {
+ Log.G().LogWarning($"[{message.severity}] {message.message}: {message.position.fileName}:{message.position.line},{message.position.column}-{message.position.endColumn},{message.position.endColumn}");
+ }
+
+ }
+ }
+ }
+ }
+
+ if(shouldFail && !PrettyPrint )
+ {
+ DieWithError("One or more validation errors found.");
+ }
+
+
+
+ }
+
+ }
+ }
+}
diff --git a/src/CodeQLToolkit.Features/Validation/Commands/ValidationCommandFeature.cs b/src/CodeQLToolkit.Features/Validation/Commands/ValidationCommandFeature.cs
index 2e8db6d..b5d8450 100644
--- a/src/CodeQLToolkit.Features/Validation/Commands/ValidationCommandFeature.cs
+++ b/src/CodeQLToolkit.Features/Validation/Commands/ValidationCommandFeature.cs
@@ -1,4 +1,5 @@
-using CodeQLToolkit.Shared.Utils;
+using CodeQLToolkit.Features.Validation.Commands.Targets;
+using CodeQLToolkit.Shared.Utils;
using System.CommandLine;
namespace CodeQLToolkit.Features.Test.Commands
@@ -29,27 +30,29 @@ public void Register(Command parentCommand)
var runCommand = new Command("run", "Functions pertaining running validation commands.");
parentCommand.Add(runCommand);
- var getMatrixTestCommand = new Command("check-metadsata", "Checks the query metadata for the specified queries.");
+ var checkQueryQueriesCommand = new Command("check-queries", "Checks the query metadata for the specified language.");
var languageOption = new Option("--language", $"The language to run tests for.") { IsRequired = true }.FromAmong(SupportedLangauges.Select(x => x.ToOptionString()).ToArray());
- var matrixOSVersion = new Option("--os-version", () => "ubuntu-latest", "A comma-seperated list of operating systems to use. Example: `ubuntu-latest`.") { IsRequired = true };
- getMatrixTestCommand.Add(matrixOSVersion);
-
- runCommand.Add(getMatrixTestCommand);
+ var prettyPrintOption = new Option("--pretty-print", () => false, "Pretty prints error output in a pretty compact format.") { IsRequired = true };
+ checkQueryQueriesCommand.Add(languageOption);
+ checkQueryQueriesCommand.Add(prettyPrintOption);
- getMatrixTestCommand.SetHandler(() =>
- {
- Log.G().LogInformation("Executing validate-unit-tests command...");
-
- //new ValidateUnitTestsCommand()
- //{
- // ResultsDirectory = resultsDirectory,
- // PrettyPrint = prettyPrint
- //}.Run();
+ runCommand.Add(checkQueryQueriesCommand);
- });
+ checkQueryQueriesCommand.SetHandler((language, basePath, prettyPrint) =>
+ {
+ Log.G().LogInformation("Executing check-query-metadata command...");
+
+ new CheckQueriesCommandTarget()
+ {
+ Base = basePath,
+ Language = language,
+ PrettyPrint = prettyPrint,
+ }.Run();
+
+ }, languageOption, Globals.BasePathOption, prettyPrintOption);
}
public int Run()
diff --git a/src/CodeQLToolkit.Features/Validation/Lifecycle/BaseLifecycleTarget.cs b/src/CodeQLToolkit.Features/Validation/Lifecycle/BaseLifecycleTarget.cs
new file mode 100644
index 0000000..649b3c0
--- /dev/null
+++ b/src/CodeQLToolkit.Features/Validation/Lifecycle/BaseLifecycleTarget.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CodeQLToolkit.Features.Validation.Lifecycle
+{
+ abstract internal class BaseLifecycleTarget : ILifecycleTarget
+ {
+ public string UseRunner { get; set; }
+
+ }
+}
\ No newline at end of file
diff --git a/src/CodeQLToolkit.Features/Validation/Lifecycle/Targets/Actions/InitLifecycleTarget.cs b/src/CodeQLToolkit.Features/Validation/Lifecycle/Targets/Actions/InitLifecycleTarget.cs
new file mode 100644
index 0000000..34fd7ce
--- /dev/null
+++ b/src/CodeQLToolkit.Features/Validation/Lifecycle/Targets/Actions/InitLifecycleTarget.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CodeQLToolkit.Features.Validation.Lifecycle.Targets.Actions
+{
+ [AutomationType(AutomationType.ACTIONS)]
+ internal class InitLifecycleTarget : BaseLifecycleTarget
+ {
+ public InitLifecycleTarget()
+ {
+ AutomationType = AutomationType.ACTIONS;
+ }
+
+ public override void Run()
+ {
+ Log.G().LogInformation("Running init command...");
+
+ // temporarily disable the language resolution
+ var tmpLanguage = Language;
+ Language = null;
+
+ WriteTemplateIfOverwriteOrNotExists("validate-query-metadata", Path.Combine(Base, ".github", "workflows", $"validate-codeql-queries-{tmpLanguage}.yml"), $"Validate CodeQL Queries ({Language})", new
+ {
+ useRunner = UseRunner,
+ language = tmpLanguage,
+ });
+
+ Language = tmpLanguage;
+
+ var message = @"------------------------------------------
+Your repository now has CodeQL Query Validation installed in `.github/workflows/`. Please ensure to initialize CodeQL
+testing before using this workflow with `qlt test init`.
+
+Note that, by default, your runner the `ubuntu-latest` runner.
+
+You can modify default runner by adjusting the `--use-runner` argument.
+
+In addition to using QLT to generate your files you can also directly edit this file to fine tune its settings.
+
+(Hint: If you'd like to regenerate your files, you can use the `--overwrite-existing` option to overwrite the files that are in place now.)";
+
+ Log.G().LogInformation(message);
+ }
+ }
+}
diff --git a/src/CodeQLToolkit.Features/Validation/Lifecycle/ValidationLifecycleFeature.cs b/src/CodeQLToolkit.Features/Validation/Lifecycle/ValidationLifecycleFeature.cs
new file mode 100644
index 0000000..ea3714b
--- /dev/null
+++ b/src/CodeQLToolkit.Features/Validation/Lifecycle/ValidationLifecycleFeature.cs
@@ -0,0 +1,75 @@
+using CodeQLToolkit.Features.Test.Lifecycle;
+using CodeQLToolkit.Shared.Utils;
+using System;
+using System.Collections.Generic;
+using System.CommandLine;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CodeQLToolkit.Features.Validation.Lifecycle
+{
+ internal class ValidationLifecycleFeature : FeatureBase, IToolkitLifecycleFeature
+ {
+ public ValidationLifecycleFeature()
+ {
+ FeatureName = "Validation";
+ }
+
+ public override LanguageType[] SupportedLangauges
+ {
+ get => new LanguageType[] {
+ LanguageType.C,
+ LanguageType.CPP,
+ LanguageType.CSHARP,
+ LanguageType.JAVA,
+ LanguageType.JAVASCRIPT,
+ LanguageType.GO,
+ LanguageType.RUBY,
+ LanguageType.PYTHON
+ };
+ }
+
+ public void Register(Command parentCommand)
+ {
+ Log.G().LogInformation("Registering lifecycle submodule.");
+
+ var initCommand = new Command("init", "Initialize validation checking in this repository.");
+
+ var overwriteExistingOption = new Option("--overwrite-existing", () => false, "Overwrite exiting files (if they exist).");
+ var languageOption = new Option("--language", $"The language to generate automation for.") { IsRequired = true }.FromAmong(SupportedLangauges.Select(x => x.ToOptionString()).ToArray());
+ var useRunnerOption = new Option("--use-runner", () => "ubuntu-latest", "The runner(s) to use. Should be a comma-seperated list of actions runners.");
+
+ initCommand.AddOption(overwriteExistingOption);
+ initCommand.AddOption(languageOption);
+ initCommand.AddOption(useRunnerOption);
+
+ parentCommand.Add(initCommand);
+
+ initCommand.SetHandler((basePath, automationType, overwriteExisting, language, useRunner) =>
+ {
+ Log.G().LogInformation("Executing init command...");
+
+ //
+ // dispatch at runtime to the correct automation type
+ //
+ var featureTarget = AutomationFeatureFinder.FindTargetForAutomationType(AutomationTypeHelper.AutomationTypeFromString(automationType));
+
+ // setup common params
+ featureTarget.FeatureName = FeatureName;
+ featureTarget.Base = basePath;
+ featureTarget.UseRunner = useRunner;
+ featureTarget.OverwriteExisting = overwriteExisting;
+ featureTarget.Language = language;
+ featureTarget.Run();
+
+ }, Globals.BasePathOption, Globals.AutomationTypeOption, overwriteExistingOption, languageOption, useRunnerOption);
+
+ }
+
+ public int Run()
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/CodeQLToolkit.Features/Validation/ValidationFeatureMain.cs b/src/CodeQLToolkit.Features/Validation/ValidationFeatureMain.cs
index 1f8367f..27e24e4 100644
--- a/src/CodeQLToolkit.Features/Validation/ValidationFeatureMain.cs
+++ b/src/CodeQLToolkit.Features/Validation/ValidationFeatureMain.cs
@@ -1,4 +1,5 @@
using CodeQLToolkit.Features.Test.Commands;
+using CodeQLToolkit.Features.Validation.Lifecycle;
using System.CommandLine;
namespace CodeQLToolkit.Features.Validation
@@ -7,6 +8,7 @@ public class ValidationFeatureMain : IToolkitFeature
{
readonly ValidationCommandFeature commandFeature;
+ readonly ValidationLifecycleFeature validationLifecycleFeature;
readonly static ValidationFeatureMain instance;
static ValidationFeatureMain()
@@ -17,6 +19,7 @@ static ValidationFeatureMain()
private ValidationFeatureMain()
{
commandFeature = new ValidationCommandFeature();
+ validationLifecycleFeature = new ValidationLifecycleFeature();
}
public static ValidationFeatureMain Instance { get { return instance; } }
@@ -28,6 +31,9 @@ public void Register(Command parentCommand)
Log.G().LogInformation("Registering command submodule.");
commandFeature.Register(validationCommand);
+ Log.G().LogInformation("Registering lifecycle submodule.");
+ validationLifecycleFeature.Register(validationCommand);
+
}
public int Run()