Skip to content

Add .NET support via turbo-managed dotnet tasks #39

Description

@gtbuchanan

Summary

Add .NET support to the tooling monorepo by wrapping dotnet commands in turbo-managed tasks, giving .NET projects the same remote caching, task orchestration, and unified CLI experience as JS/TS packages.

Package structure

  • .csproj packages live under packages/*/ alongside JS packages
  • sync auto-discovers .csproj files and generates package.json wrappers (workspace identity only)
  • .csproj name matches PackageId, consumer-created
  • package.json name derived from directory name (@gtbuchanan/my-package)
  • Test project in test/ (one .csproj per package, categories for fast/slow filtering)
  • E2E test project in e2e/ (artifact tests against packed NuGet)

Example layout:

packages/my-package/
  package.json              # generated by sync
  src/GtBuchanan.MyPackage/
    GtBuchanan.MyPackage.csproj
  test/GtBuchanan.MyPackage.Test/
    GtBuchanan.MyPackage.Test.csproj
  e2e/GtBuchanan.MyPackage.E2E/
    GtBuchanan.MyPackage.E2E.csproj

Turbo integration

  • sync discovers .csproj files under workspace globs, generates package.json + scripts
  • sync scans <ProjectReference> entries in .csproj files and adds corresponding workspace:* dependencies to the generated package.json, so turbo infers dependsOn automatically
  • sync generates/updates .slnx at repo root mirroring directory structure for IDE support
  • dotnet restore runs before turbo (like pnpm install)
  • Cross-ecosystem dependencies (e.g., .NET app consuming TS client output) work via workspace:* in package.json

Cache inputs

  • globalDependencies: Directory.Build.props, Directory.Build.targets, global.json, root .editorconfig
  • Per-task inputs: .csproj, packages.lock.json, source files, package-level .editorconfig
  • Intermediate .editorconfig files (e.g., packages/.editorconfig) must be added to globalDependencies manually (document this)
  • .NET tasks include env: ["CI"] so turbo hashes build configuration into the cache key

Generated config

Directory.Build.props

  • RestorePackagesWithLockFile enabled
  • Per-test-project MTP runner property detected from framework reference:
    • MSTest → EnableMSTestRunner
    • NUnit → EnableNUnitRunner
    • xUnit → UseMicrosoftTestingPlatformRunner

global.json

  • test.runner set to Microsoft.Testing.Platform

.slnx

  • Generated at repo root, mirrors packages/*/ directory structure

CLI commands

Command Underlying Notes
compile:dotnet dotnet build --configuration Release when CI=true
lint:dotnet-format dotnet format --verify-no-changes Report output for lint regression check (#38)
test:dotnet dotnet test --no-build fast + slow
test:dotnet:fast dotnet test --no-build --filter "TestCategory!=Slow" Framework-detected filter syntax
test:dotnet:slow dotnet test --no-build --filter "TestCategory=Slow" Framework-detected filter syntax
test:dotnet:e2e dotnet test --no-build (e2e project) Against packed artifacts
pack:nuget dotnet pack --no-build
  • --no-build on test/pack commands because turbo already runs compile:dotnet as a dependency
  • compile:dotnet passes --configuration Release when CI environment variable is set, Debug otherwise
  • Commands abstract filter syntax per test framework (MSTest/NUnit/xUnit)
  • Consumers can override any script in package.json for unsupported frameworks or custom needs
  • sync preserves existing script values (no --force needed to keep overrides)
  • Multitargeting (<TargetFrameworks>) works transparently — dotnet build/test handle all targets, turbo caches the combined output

Design decisions

  • Standardize on Microsoft.Testing.Platform (MTP) — VSTest is legacy
  • MTP runner enable property added per-test-project .csproj (not Directory.Build.props) to support mixed-framework migration paths
  • Directory.Packages.props is NOT a globalDependency — per-project packages.lock.json already captures its effects
  • NuGet PackageId is consumer-controlled in .csproj, not derived from directory name
  • Cross-package .NET references managed via <ProjectReference> in .csproj; sync syncs these to package.json workspace:* dependencies automatically
  • RID-specific builds (dotnet publish) deferred to a future issue — pack:nuget output is platform-agnostic

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions