This document explains the CVOYA graph build configurations, version management, and development workflows.
To get started, install the Neo4j provider package (required):
dotnet add package Cvoya.Graph.Neo4jOptionally, add the analyzers package for extra compile-time validation (recommended):
dotnet add package Cvoya.Graph.Analyzers# Development build (fastest, project references)
dotnet build --configuration Debug
# Test package references locally
dotnet msbuild eng/PackageValidation.proj -target:Validate
# Cut a release (--plan previews the tag without pushing)
./scripts/release.sh 1.2.3
# Build one production package (pack builds first by default)
dotnet pack src/Graph/Graph.csproj --configuration Release
# Run performance benchmarks
./scripts/run-benchmarks.sh
# Run local CodeQL analysis
./scripts/run-codeql.shCVOYA graph uses four distinct build configurations optimized for different scenarios:
| Configuration | Project Refs | Optimizations | Packages | VERSION Required | Use Case |
|---|---|---|---|---|---|
| Debug | ✅ Yes | ❌ No | ❌ No | ❌ No | Development & debugging |
| Benchmark | ✅ Yes | ✅ Yes | ❌ No | ❌ No | Performance testing |
| LocalFeed | ✅ Yes | ✅ Yes | ✅ Yes | ❌ No | Local package testing |
| Release | ✅ Default | ✅ Yes | Via pack |
✅ Yes | Production package builds |
- Purpose: Day-to-day development
- Speed: Fastest builds (no optimizations)
- Dependencies: Direct project references
- Packages: None generated
- Usage:
dotnet build --configuration Debug
- Purpose: Performance testing and benchmarking
- Speed: Optimized builds with project references
- Dependencies: Direct project references (fast rebuilds)
- Packages: None generated
- Usage:
dotnet msbuild -target:BuildBenchmark
- Purpose: Building the local package set before package-reference validation
- Speed: Optimized builds with fast project references
- Dependencies: Direct project references (fast builds)
- Packages: Generated in the configured package output directory
- Usage:
dotnet build --configuration LocalFeed
- Purpose: Production builds and publishing
- Speed: Fully optimized
- Dependencies: Project references by default; the package-validation orchestrator explicitly sets
UsePackageReferences=true - Packages: Generated by
dotnet pack; a normal Release build does not pack - Usage:
dotnet pack src/Graph/Graph.csproj --configuration Release
CVOYA graph releases are tag-authoritative: the pushed tag is the version. Nothing in CI writes or stamps a version, which is what keeps package versions consistent and prevents accidental releases.
MAJOR.MINOR.PATCH[-(alpha|beta|rc).YYYYMMDD[.N]]
# Stable release
1.2.3
# Date-anchored pre-releases (the .1 is a same-day counter)
1.2.3-alpha.20260716
1.2.3-rc.20260716.1
scripts/release.sh computes the version, pushes the tag, and release.yml reads
it back — nothing writes a version to a file, so nothing can be stamped twice.
# Preview the computed tag without pushing anything
./scripts/release.sh 1.2.3 --pre alpha --plan
# Cut a stable release -> v1.2.3
./scripts/release.sh 1.2.3
# Cut a date-anchored pre-release -> v1.2.3-alpha.20260716
./scripts/release.sh 1.2.3 --pre alpha
# ...and make it the current Latest on GitHub
./scripts/release.sh 1.2.3 --pre alpha --latestThe base version is positional and pre-release suffixes come from --pre — they
are never typed by hand. See ./scripts/release.sh --help for the full option
list, and release-process.md for the full process.
VERSION is the development default for untagged local and CI builds, not the
release source of truth. Editing it does not cut a release, and cutting a release
does not edit it. release.yml passes the tag's version to the pack job as
GRAPH_RELEASE_VERSION, which Directory.Build.props prefers over the file.
Publishing is entirely tag-triggered — there is no manual publish step:
- Run
./scripts/release.sh X.Y.Z [--pre alpha|beta|rc]from a checkout whose HEAD is onorigin/main. It computes the version, runs the pre-flight checks, and pushes thevX.Y.Ztag. .github/workflows/release.ymlreads the version off the tag and rejects it if it doesn't match the version scheme, runs the full test suite including the Neo4j and Apache AGE provider tests, packs every package, publishes to NuGet using Trusted Publishing (OIDC — no stored API key), attests build provenance, and creates the GitHub Release with generated notes.release.shwatches that run and then verifies every published package actually resolves on nuget.org before reporting success.
See docs/release-process.md for the full process, including the one-time NuGet Trusted Publishing portal setup.
# 1. Regular development (fastest)
dotnet build --configuration Debug
# 2. Run the service-free/in-memory lane
./scripts/run-tests.sh --configuration Debug --lane fast
# 3. Performance testing when needed
dotnet build --configuration Benchmark
./scripts/run-benchmarks.shCVOYA graph's GitHub workflow runs CodeQL for GitHub Actions, C#, and Ruby with
the security-and-quality query suite. To catch those findings before pushing,
install the CodeQL CLI and run:
./scripts/run-codeql.shThe script writes one SARIF file per language under artifacts/codeql/results/.
It downloads the Actions, C#, and Ruby query packs by default so local scans use
the same query suite as .github/workflows/codeql.yml. The default CodeQL build
mode is none, which is the required portable local gate. In that mode, the
script analyzes a disposable source copy and temporary databases outside the
checkout so CodeQL dependency probing cannot rewrite repository files.
To trace the same LocalFeed and Release builds used by the GitHub workflow,
use manual build mode:
./scripts/run-codeql.sh --build-mode manualManual mode is optional because it relies on CodeQL compiler tracing support for
the local platform and .NET toolchain. If manual mode completes the build but
CodeQL exits with "could not process any" C# source, rerun the default command
without --build-mode manual. A successful default run satisfies the local
CodeQL gate; capture the manual-mode CodeQL version and db/csharp/log path in
PR notes only if the manual failure is relevant.
For a stricter local gate:
./scripts/run-codeql.sh --fail-on-alertsTo include CodeQL in the full build-system validation pass:
./scripts/validate-build.sh --codeql# 1. Verify the release build works
dotnet build --configuration Release
# 2. Start both services and run the full discovered suite
./scripts/run-tests.sh --configuration Release --lane all --neo4j --age
# 3. Preview the tag that would be cut
./scripts/release.sh 1.2.3 --plan
# 4. Cut it — release.sh pushes the tag, watches release.yml, and verifies
# the published packages resolve on nuget.org
./scripts/release.sh 1.2.3release.sh requires a clean checkout exactly at the refreshed origin/main
commit; it refuses stale, divergent, or dirty state. Steps 1 and 2 are a local
smoke test — release.yml reruns the full suite against the tagged commit
regardless.
Package-reference validation has one repository-level owner:
dotnet msbuild eng/PackageValidation.proj -target:ValidateThe orchestrator starts from clean repository-owned state, restores and builds the LocalFeed
package set, explicitly packs the analyzer projects, and then restores/builds the solution with
UsePackageReferences=true. scripts/verify-package-set.sh derives the expected inventory from
every packable project under src/, rejects missing or unexpected packages, and verifies every
packaged assembly's version metadata. eng/package-validation.NuGet.config maps Cvoya.* to the
generated feed and everything else to NuGet.org.
All package, feed, global-packages, HTTP-cache, scratch, and plugin-cache paths live under artifacts/package-validation/. The workflow does not add or remove user-level NuGet sources and does not read, clear, or write the user's global NuGet caches.
The inventory check requires bash and jq on PATH; this is the same toolchain used by the repository scripts on Linux, macOS, and Git-for-Windows/GitHub-hosted Windows environments. All path construction and project classification inside MSBuild use OS-native normalized paths.
The compatibility wrappers delegate to the same orchestrator:
./scripts/setup-local-feed-msbuild.sh
./scripts/cleanup-local-feed.sh# Cut a release (pushes the tag; --plan to preview)
./scripts/release.sh X.Y.Z [--pre alpha|beta|rc]
# Show the version the current tree would build as
dotnet msbuild -target:ShowVersion# Build specific configurations
dotnet build --configuration Debug
dotnet build --configuration Benchmark
dotnet build --configuration Release# Pack and verify the repository-scoped local feed
dotnet msbuild eng/PackageValidation.proj -target:PrepareLocalFeed
# Build the solution with locally produced package references
dotnet msbuild eng/PackageValidation.proj -target:BuildWithPackageReferences
# Run the complete required gate
dotnet msbuild eng/PackageValidation.proj -target:Validate
# Remove only repository-scoped validation state
dotnet msbuild eng/PackageValidation.proj -target:Clean# Clean build outputs
dotnet cleangraph/
├── VERSION # Development default version for untagged builds
├── Directory.Build.props # MSBuild configuration
├── eng/
│ ├── PackageValidation.proj # Local package-validation orchestrator
│ ├── PackageVersionVerifier/ # Reads version metadata from packed DLLs
│ └── package-validation.NuGet.config
├── artifacts/package-validation/ # Repository-scoped feed, packages, and caches
└── scripts/
├── release.sh # Release orchestration (tag, watch, verify)
├── run-benchmarks.sh # Benchmark runner
└── cleanup-local-feed.sh # Compatibility wrapper over PackageValidation.proj
# Supply a version for this package without editing VERSION
GRAPH_RELEASE_VERSION=1.2.3 dotnet pack src/Graph/Graph.csproj --configuration Release# Recreate the isolated feed and package-reference restore
dotnet msbuild eng/PackageValidation.proj -target:ValidateUse the normal pack command when building and packing in one step:
GRAPH_RELEASE_VERSION=1.2.3 dotnet pack --configuration Release -o ./artifacts src/Graph/Graph.csproj--no-build is reserved for flows that already built with the same
GRAPH_RELEASE_VERSION, such as release.yml. The complete local package gate
also verifies the NuGet manifest and the assembly, file, and informational
versions inside every package:
dotnet msbuild eng/PackageValidation.proj -target:Validate# Solution: Clean and rebuild
dotnet clean
dotnet build --configuration Release# Force package generation in any configuration
dotnet build -p:ForcePackageGeneration=true
# Override package output path
dotnet pack src/Graph/Graph.csproj --configuration Release -p:PackageOutputPath=/custom/pathReleases are handled entirely by .github/workflows/release.yml, triggered by a
validated vX.Y.Z[-prerelease] tag — there is no CI step that stamps a version.
See docs/release-process.md.
- Use Debug configuration for daily development
- Use Benchmark configuration for performance testing
- Keep VERSION file in source control
- Test with Release configuration before publishing
- Preview releases with
./scripts/release.sh X.Y.Z [--pre alpha|beta|rc] --plan - Let
release.shgenerate date-anchored prerelease versions; do not type tags by hand - Test Release configuration before publishing
- Never move an existing release tag; rerun its workflow or choose a new version
- Debug: Fastest for development
- Benchmark: Fast + optimized for testing
- Release: Optimized production builds and explicit packing
- Check if VERSION exists for Release/package-validation builds.
- Clean and rebuild:
dotnet clean && dotnet build. - For package-reference failures, run
dotnet msbuild eng/PackageValidation.proj -target:Clean, then rerun-target:Validate.
- Remove repository package-validation state:
dotnet msbuild eng/PackageValidation.proj -target:Clean. - Rerun the complete package gate:
dotnet msbuild eng/PackageValidation.proj -target:Validate. - Inspect
artifacts/package-validation/; do not clear user-level NuGet caches.
- Check the resolved version:
dotnet msbuild -target:ShowVersion - Override it for one build:
GRAPH_RELEASE_VERSION=X.Y.Z dotnet build -c Release - Verify VERSION file format (single line, no extra whitespace)