Skip to content

Tamp CodeQL

Scott Singleton edited this page May 15, 2026 · 2 revisions

Tamp.CodeQL

Wrapper for CodeQL 2 — GitHub's static analysis engine. PAT is typed as Secret and fed to the binary via --github-auth-stdin rather than argv, so it doesn't leak into process listings.

using Tamp.CodeQL.V2;

Full reference: https://github.com/tamp-build/tamp-codeql.

Sub-facades

CodeQL.Database.Create / Init / TraceCommand / Finalize / Analyze / Upgrade / ExportDiagnostics / Bundle
CodeQL.GitHub.UploadResults     // POST SARIF to Code Scanning API (Secret via stdin)
CodeQL.Resolve.Languages / Queries
CodeQL.Pack.Download / Install
CodeQL.Query.Run
CodeQL.Version
CodeQL.Raw                       // escape hatch (bqrs decode, test, dataset, …)

Common flags (all verbs): --threads, --ram, --verbosity, --quiet, --no-progress-tracker, --logdir, --common-caches.

CI behaviour to know about

Bundle is heavy. The full CodeQL bundle (CLI + standard library packs + extractors) is ~500MB. CI workflows that just need the CLI binary should pull codeql-cli-binaries instead of the full bundle and download QL packs on demand via Pack.Download.

Token via stdin. GitHub.UploadResults accepts Secret? GitHubToken. When set, the wrapper emits --github-auth-stdin and pipes the token to the child process's stdin — the value never appears as an argv token. The Secret also joins the redaction table so any subsequent log line that echoes the value gets scrubbed.

--[no-]flag rendering in help. CodeQL renders boolean flags as --[no-]overwrite in --help output, which means Assert.Contains("--overwrite", ...) fails as a substring match. If you're writing wrapper-shape tests against --help -v output, use a regex that tolerates the [no-] prefix.

Quick example — analyze + upload SARIF

[NuGetPackage("codeql", UseSystemPath = true)]
readonly Tool CodeQLTool = null!;

[Secret("GitHub token", EnvironmentVariable = "GITHUB_TOKEN")]
readonly Secret GitHubToken = null!;

[GitRepository] readonly GitRepository Git = null!;

Target CreateDb => _ => _.Executes(() =>
    CodeQL.Database.Create(CodeQLTool, s => s
        .SetDatabasePath("codeql-db")
        .SetLanguage("csharp")
        .SetSourceRoot(".")
        .SetCommand("dotnet build")
        .SetOverwrite()
        .SetRam(8000)
        .SetThreads(0)));

Target Analyze => _ => _
    .DependsOn(nameof(CreateDb))
    .Executes(() =>
        CodeQL.Database.Analyze(CodeQLTool, s => s
            .SetDatabasePath("codeql-db")
            .AddQuery("codeql/csharp-queries")
            .SetFormat("sarif-latest")
            .SetOutput("results.sarif")
            .SetSarifCategory("primary")
            .SetSarifAddSnippets()));

Target Upload => _ => _
    .DependsOn(nameof(Analyze))
    .Requires(() => GitHubToken != null)
    .Executes(() =>
        CodeQL.GitHub.UploadResults(CodeQLTool, s => s
            .SetSarifFile("results.sarif")
            .SetRepository("acme/widgets")
            .SetRef("refs/heads/main")
            .SetCommit(Git.Commit)
            .SetGitHubToken(GitHubToken)
            .SetWaitForProcessing()
            .SetWaitForProcessingTimeout(120)));

See also

Settings authoring style

The example(s) above use the fluent Set*-chain shape. Every wrapper verb on this page also accepts a new XxxSettings { ... } object-init form. Both produce identical CommandPlans; the fluent shape stays canonical in docs and the tamp init template. See Build Script Authoring → Two authoring styles for the side-by-side comparison.

Clone this wiki locally