Skip to content

Reference Entrypoints

These are thin consumer-owned templates for ArchLinterNet. They pin or restore the tool, pass structured arguments, invoke one validation command per requested session, and preserve stdout/stderr. When one session requires both strict and audit over the same build state, that command is the combined --mode strict,audit --ensure-built path. POSIX, PowerShell, Task with --exit-code, and direct CI invocations propagate the exact product exit code. GNU Make cannot do so for a failing recipe, so its template writes that exact code to an artifact for the outer shell/CI caller to propagate. The templates do not generate a policy, create a baseline, approve debt, or write an API snapshot automatically.

Examples use the synthetic Example.Product policy path. Replace values with your own reviewed paths; do not interpolate untrusted values into shell command strings or use eval.

Direct pinned .NET tool

Commit .config/dotnet-tools.json after selecting the package. The manifest records the exact resolved version; CI restores that reviewed pin rather than a version copied from documentation:

dotnet new tool-manifest
dotnet tool install ArchLinterNet.Cli
dotnet tool restore
dotnet arch-linter-net --policy architecture/arch.yml --mode strict --ensure-built

For a workflow that requires both mode results from one build state, use the canonical combined invocation and route product reports directly:

dotnet arch-linter-net --policy architecture/arch.yml \
  --mode strict,audit --ensure-built \
  --report json=artifacts/architecture-results.json \
  --report sarif=artifacts/architecture-results.sarif

This performs one snapshot-owned build/preflight preparation (including post-build receipt verification) and evaluates strict and audit from one immutable analysis snapshot. The command fails if either requested mode fails; the JSON and SARIF sinks reuse those completed outcomes. Single-mode commands remain supported when only one mode is needed.

When upgrading, use dotnet tool update ArchLinterNet.Cli, review the manifest diff, and run the repository acceptance gate before merging the new pin.

For JSON and SARIF artifacts, use product report routing rather than shell redirection where multiple files are needed:

dotnet arch-linter-net --policy architecture/arch.yml --mode strict --ensure-built \
  --report json=artifacts/architecture.json \
  --report sarif=artifacts/architecture.sarif

Exit code 0 is a passed gate, 1 is a completed failing gate, and 2 means the command could not complete. A pipeline must preserve all three values.

POSIX shell

Use an argument array. It keeps a policy path containing spaces as one argument and neither evaluates arguments nor merges the tool's standard streams.

#!/usr/bin/env bash
set -u

tool=(dotnet arch-linter-net)
args=(--policy "architecture/Example Product/arch.yml" --mode strict --ensure-built)

"${tool[@]}" "${args[@]}"
exit "$?"

The script deliberately does not use set -e: the caller must receive the tool's exact 1 or 2 status rather than a shell-specific substitute. Do not replace the array invocation with eval, sh -c, or a concatenated command string.

PowerShell

Pass native arguments as an array, invoke the native executable directly, and return $LASTEXITCODE. This preserves native stdout/stderr and works when the output is redirected or no terminal is attached.

$ErrorActionPreference = 'Stop'

$arguments = @(
    'arch-linter-net',
    '--policy', 'architecture/Example Product/arch.yml',
    '--mode', 'strict'
)

& dotnet @arguments
$exitCode = $LASTEXITCODE
exit $exitCode

Do not use Invoke-Expression, concatenate $arguments into one string, or replace $LASTEXITCODE with PowerShell's success preference. A validation failure must remain exit 1; cancellation, malformed input, and output failure must remain exit 2.

Make

GNU Make does not preserve the product exit code when a recipe fails: its own process exits 2 for both product status 1 and 2. Keep the exact product status in a machine-readable artifact, then make the outer shell or CI caller return that saved value. Do not use a plain failing recipe as a three-state CI interface.

.PHONY: architecture

architecture:
    @mkdir -p artifacts
    @set +e; \
      dotnet arch-linter-net --policy architecture/arch.yml --mode strict --ensure-built; \
      status=$$?; \
      printf '%s\n' $$status > artifacts/architecture.exit-code; \
      exit 0

Restore the pinned tool before the target, then call Make and re-emit its saved product status from the surrounding POSIX shell or CI step:

dotnet tool restore
make architecture
status=$(cat artifacts/architecture.exit-code)
exit "$status"

The product is still invoked exactly once. The artifact is a status channel, not a replacement for JSON/SARIF reports.

Taskfile

Task's normal failure exit code is Task's own code, not necessarily the product status. Invoke the task with --exit-code to preserve the ArchLinterNet 0/1/2 contract. Use a fixed command rather than a shell-composed string:

version: '3'

tasks:
  architecture:
    cmds:
      - dotnet arch-linter-net --policy architecture/arch.yml --mode strict --ensure-built

The command is a fixed literal: do not append untrusted values through Task's template interpolation. Keep tool restore as a distinct bootstrap task when it is not already performed by the environment. The architecture task invokes ArchLinterNet once, and its caller must use:

task --exit-code architecture

Tilt

Use a fixed argv list and let Tilt display the tool output. Do not construct a shell string from repository or environment values:

local([
    "dotnet",
    "arch-linter-net",
    "--policy", "architecture/arch.yml",
    "--mode", "strict",
])

Tilt remains consumer orchestration: it does not change policy, baseline, API snapshot, cache, or report semantics.

Generic CI contract

Every CI provider needs the same sequence: restore the pinned tool, restore dependencies as appropriate, let --ensure-built own the selected graph build, invoke either strict validation once or the combined strict-and-audit validation once when both modes are required from the same build state, preserve the product exit code, and retain configured report files as artifacts. The provider does not parse display prose to decide success.

dotnet tool restore
dotnet restore
dotnet arch-linter-net --policy architecture/arch.yml --mode strict,audit --ensure-built --no-restore \
  --report json=artifacts/architecture.json \
  --report sarif=artifacts/architecture.sarif
status=$?
upload artifacts/architecture.json and artifacts/architecture.sarif when present
return status unchanged

For a resource-constrained runner, choose the supported sequential mode:

dotnet arch-linter-net --policy architecture/arch.yml --mode strict --ensure-built --max-parallelism 1

For a prepared offline runner, use the installed tool's schema registry and preserve the no-restore boundary:

dotnet arch-linter-net schema list
dotnet arch-linter-net schema print policy-root > artifacts/policy-root.schema.json
dotnet arch-linter-net --policy architecture/arch.yml --mode strict --ensure-built --no-restore

Cache is disabled unless explicitly selected. If the CI trust boundary permits a caller-owned cache, opt in deliberately and retain profile evidence separately from reports:

dotnet arch-linter-net --policy architecture/arch.yml --mode strict --ensure-built \
  --cache .architecture-cache \
  --profile artifacts/architecture-profile.json

A cancelled run or failed report publication exits 2. If a multi-report run reports partial-output, preserve both the command status and its committed / uncommitted destination evidence; do not rerun it automatically.

GitHub Actions example

GitHub Actions is one provider of the generic contract, not a semantic product dependency:

name: Architecture validation

on: [pull_request]

jobs:
  architecture:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      - uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
        with:
          dotnet-version: 10.0.x
      - run: dotnet tool restore
      - run: dotnet restore
      - name: Combined strict + audit validation
        run: >-
          dotnet arch-linter-net --policy architecture/arch.yml --mode strict,audit --ensure-built --no-restore
          --report json=artifacts/architecture.json
          --report sarif=artifacts/architecture.sarif
      - name: Upload diagnostics
        if: always()
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: architecture-diagnostics
          path: artifacts/

An audit run is a visibility decision, not a replacement for strict validation. If audit is intentionally advisory, keep separate strict-blocking and non-blocking-audit steps while retaining each tool exit code in logs and artifacts. Those are independent CLI processes and do not reuse prepared state across processes. The combined command is the choice when both modes must contribute to one required CI decision.

Testing API

The Testing API loads the same policy and has the same load-bearing semantics as the CLI. Snapshot ownership is explicit; normal tests can validate one mode directly:

using ArchLinterNet.Testing;
using NUnit.Framework;

[TestFixture]
public sealed class ArchitectureTests
{
    [Test]
    public void StrictArchitectureContractsMustPass()
    {
        ArchitectureAssertions
            .FromPolicy("architecture/arch.yml")
            .ValidateStrict()
            .ShouldPass();
    }
}

Use .WithBaseline(path).VerifyBaseline() for a read-only baseline gate and keep capture/update/migrate ownership in a reviewed local workflow. See Test Adapter and the upgrade guide.