src/Ando/: Ando CLI (.NET tool) and core libraries.src/Ando.Server/: ASP.NET Core server (EF Core + SQL Server), FastEndpoints-based route surface (API + compatibility/infrastructure), React client insrc/Ando.Server/ClientApp/.tests/:tests/Ando.Tests/: unit tests for CLI/core.tests/Ando.Server.Tests/: server tests (seetests/Ando.Server.Tests/TESTING.md).tests/Ando.Server.E2E/: Playwright E2E tests (Node).
scripts/: automation and deployment helpers (e.g. server installer).website/: docs site.
dotnet build Ando.sln
dotnet test
docker compose up -d # local dev SQL Server + Ando.Server
docker compose logs -fE2E (example):
docker compose -f tests/docker-compose.test.yml up -d --build
cd tests/Ando.Server.E2E && npm ci && npm test- Global port allocations are tracked in
~/Source/PORTS.md. - ANDO reserved range:
17100-17199. - Keep new local/dev/test port mappings within the reserved ANDO range unless there is a deliberate exception.
- C#: follow existing conventions; nullable reference types are enabled. Use
async/awaitend-to-end for IO. - Web client: TypeScript/React; keep lint clean (
src/Ando.Server/ClientApp/eslint.config.js). - Website: Prettier is used (
website/package.json).
- Prefer adding/adjusting unit tests alongside behavior changes.
- Name tests descriptively; keep E2E flows stable and deterministic (no time-dependent assertions).
- After any substantial or cross-cutting change, always run the Playwright E2E suite before handing off:
cd tests/Ando.Server.E2E && npm test
- Commit messages generally follow
type(scope): summary(e.g.fix(server): ...,refactor(email): ...) and version bumps are explicit (Bump version to X.Y.Z). - PRs should describe the behavior change, include repro steps, and note any config/env var changes.
- Never push by yourself. Do not run
git push,docker push,docker buildx --push,dotnet nuget push, or create/upload releases. - If publishing is needed: ask the user to push, then wait. After the user confirms, you may pull/restart the image on the Ando server.
- Production server access context: Before making assumptions about production host access details, check
CLAUDE.mdfirst, then apply the deployment guidance in this file.
Use this generic guidance for any host running Ando Server. Do not hard-code environment-specific IPs, usernames, or SSH key paths in repository docs.
Current production host details (requested override):
- Host:
192.168.1.150 - User:
ando - SSH key:
~/.sshkeys/id_ad_dualconsult_com
Common management commands (run over SSH on your server):
# Example SSH
ssh -i <path-to-ssh-key> -o IdentitiesOnly=yes <user>@<server-host-or-ip>
# Compose file location (default install path)
COMPOSE_FILE=/opt/ando/docker-compose.yml
# Status / logs
docker compose -f "$COMPOSE_FILE" ps
docker compose -f "$COMPOSE_FILE" logs -f ando-server
# Update to latest pushed image (registry-based)
docker compose -f "$COMPOSE_FILE" pull ando-server
docker compose -f "$COMPOSE_FILE" up -d ando-serverIf the host uses rootless Docker, set DOCKER_HOST appropriately for that host before running docker compose.
The following content was moved from CLAUDE.md and preserved here.
Important: Always address the user as "Mr. Ando".
- GitHub Username:
aduggleby - Repository:
aduggleby/ando - Container Registry:
ghcr.io/aduggleby/ando-server
- Never automatically git push - Only push when explicitly asked
- Only git commit when explicitly asked - Do not commit changes automatically
ANDO is a typed C# build system using Roslyn scripting. Build scripts (build.csando files) are real C# code executed via Roslyn in Docker containers.
- Roslyn Scripting (
Microsoft.CodeAnalysis.CSharp.Scripting) executesbuild.csandofiles - Step Registration Pattern: Operations (Dotnet, Ef) register steps in a registry, WorkflowRunner executes them
- Docker Isolation: All builds run in containers via
DockerManagerandContainerExecutor
src/Ando/ # Main CLI and library
Cli/ # CLI entry point
Config/ # Project configuration (ando.config)
Context/ # BuildPath, PathsContext, VarsContext
Execution/ # ProcessRunner, DockerManager, ContainerExecutor
Logging/ # ConsoleLogger, IBuildLogger
Operations/ # DotnetOperations, EfOperations, NpmOperations
References/ # ProjectRef, EfContextRef
Scripting/ # ScriptHost (Roslyn integration)
Steps/ # BuildStep, StepRegistry
Utilities/ # DindChecker, GitHubScopeChecker, SDK ensurers
Workflow/ # WorkflowRunner, WorkflowConfig
tests/Ando.Tests/ # Test project
Unit/ # Unit tests (Category=Unit)
Integration/ # Integration tests (Category=Integration)
E2E/ # End-to-end tests (Category=E2E)
TestFixtures/ # Shared MockExecutor, TestLogger
examples/ # Example projects with build.csando files
website/ # Documentation website (see website/CLAUDE.md)
IMPORTANT: When asked to run tests, run ALL tests including Playwright E2E tests.
# Run all .NET tests (unit + integration)
dotnet test
# Run with coverage
dotnet test /p:CollectCoverage=true
# Run specific test categories
dotnet test --filter "Category=Unit"
dotnet test --filter "Category=Integration"E2E tests use docker-compose to create an isolated network where SQL Server and the server communicate privately. Only the server's web port (17110) is exposed.
# Install dependencies (required first time)
cd tests/Ando.Server.E2E && npm install
# Run all E2E tests (starts docker-compose automatically)
cd tests/Ando.Server.E2E && npm test
# Run with UI for debugging
cd tests/Ando.Server.E2E && npm run test:ui
# Run headed (visible browser)
cd tests/Ando.Server.E2E && npm run test:headed
# Clean up containers after tests
cd tests && docker compose -f docker-compose.test.yml down- Integration tests (.NET): Use Testcontainers to spin up SQL Server automatically
- E2E tests (Playwright): Use docker-compose with isolated network
ando-e2e-sqlserver: SQL Server (no public ports, internal network only)ando-e2e-server: Web server (port 17110 exposed)
This project uses port range 17100-17199. Other projects use different ranges to avoid conflicts.
Uses:
- xUnit for testing framework
- FsCheck for property-based testing
- Shouldly for assertions
- MockExecutor for testing without executing commands
- Testcontainers.MsSql for SQL Server integration tests
- Playwright for browser-based E2E tests
# Build project
dotnet build
# Run tests
dotnet test tests/Ando.Tests/Ando.Tests.csproj
# Create self-contained executable
dotnet publish src/Ando/Ando.csproj -c Release -r linux-x64 --self-contained -o distUse host-specific SSH details from your own environment. Do not hard-code IPs, SSH users, or key paths in repository docs.
# On your local machine: build and push the image to the registry (ghcr.io).
# Then, on the server: pull and restart the ando-server service.
cd /opt/ando
docker compose -f /opt/ando/docker-compose.yml pull ando-server
docker compose -f /opt/ando/docker-compose.yml up -d ando-server
docker compose -f /opt/ando/docker-compose.yml ps- Docker Compose file:
/opt/ando/docker-compose.yml - Environment config:
/opt/ando/config/.env - GitHub App key:
/opt/ando/config/github-app.pem - Data directories:
/opt/ando/data/artifacts- Build artifacts/opt/ando/data/repos- Cloned repositories/opt/ando/data/keys- Data protection keys/opt/ando/data/sqldata- SQL Server data
- The compose file lives at
/opt/ando/docker-compose.ymland is the source of truth for ports, volumes, and image tags. - SQL Server runs in a separate container (
ando-sqlserver). - The server port/proxy setup depends on the host configuration (see
/etc/caddy/Caddyfileif using Caddy).
-
ICommandExecutor: Interface for executing shell commandsProcessRunner: Host execution (for Docker CLI commands)ContainerExecutor: Docker exec (for build commands in container)MockExecutor: Test double
-
IBuildLogger: Logging interfaceConsoleLogger: Production loggerTestLogger: Test double that captures events
- Every file must have a header summary comment (see Documentation Standards below)
- Use records for immutable data
- Prefer async/await for I/O operations
- Operations register steps; they don't execute immediately
- Use fluent builders for options (e.g.,
WithConfiguration(),WithRuntime())
IMPORTANT: This project uses .NET 9 exclusively. Do NOT upgrade to .NET 10 or any future version.
- All projects target
net9.0 global.jsonpins the SDK to 9.x versions- If prompted to upgrade, decline and keep .NET 9
IMPORTANT: When making changes to the codebase:
-
Build and run tests first - Always run
dotnet buildanddotnet testto verify changes work before updating documentation -
Then update all related documentation and examples:
-
Website documentation (
website/src/data/operations.js) - Update operation descriptions and examples -
Website landing page (
website/src/pages/index.astro) - Update the example build.csando section -
LLM documentation (
website/public/llms.txt) - ALWAYS update when website content changes - this is the LLM-friendly reference -
Example build.csando files (
examples/*/build.csando) - Update all example scripts -
Main build.csando (
build.csando) - Update if affected -
Test files - Update test assertions and embedded scripts
-
CLAUDE.md files - Update if instructions change
-
CLI help - Update the
HelpCommand()method inAndoCli.cswhen adding or changing CLI commands/options
Run npm run build in the website directory to verify documentation builds correctly.
CRITICAL: The website/public/llms.txt file must be kept in sync with the website. This file follows the llms.txt standard and provides a plain-text reference for LLMs. When updating:
- CLI commands or options
- Operations or their signatures
- Examples or common patterns
- Provider documentation
...ensure the same changes are reflected in llms.txt.
IMPORTANT: When modifying the ANDO CLI:
-
Update
ando help- TheHelpCommand()method insrc/Ando/Cli/AndoCli.csmust always be updated when:- Adding new CLI commands
- Adding new CLI options
- Changing existing command behavior
- Adding new operations that affect CLI usage
-
DIND Mode Detection - Operations that require Docker-in-Docker are automatically detected by
DindChecker:- Scans registered steps before container creation
- Also scans child builds - Parses
Ando.Build(Directory("..."))calls and recursively checks childbuild.csandofiles for DIND operations - Checks for
--dindflag,ando.configwithdind: true, orANDO_DIND=1environment variable - Child builds inherit DIND - When parent enables DIND, it sets
ANDO_DIND=1in both the container environment AND the host process environment. The host process env is needed becauseAndo.Buildstep functions run on the host (they spawn childandoprocesses). This prevents child builds from re-prompting for DIND. - Prompts user with options: (Y)es for this run, (a)lways (saves to ando.config), Esc to cancel
- Current DIND operations: Docker.Build, Docker.Push, Docker.Install, GitHub.PushImage, Playwright.Test
-
DIND Operations Registry - When adding new operations that require Docker-in-Docker:
- Add the operation name to
DindRequiredOperationsinsrc/Ando/Utilities/DindChecker.cs - Also add the operation to
DindOperationPatternregex in the same file (for child build scanning) - Operations requiring DIND include any that need to run
dockercommands inside the build container - Examples: Docker.Build, Docker.Push, Docker.Install, GitHub.PushImage, Playwright.Test (uses docker-compose for E2E tests)
- Add the operation name to
All C# files must include comprehensive documentation using C# comment syntax:
Every file must start with a summary block explaining:
- What the file/class does
- Its role in the overall architecture
- Key design decisions
// =============================================================================
// FileName.cs
//
// Summary: Brief description of what this file contains and its purpose.
//
// This class/file is responsible for [main responsibility]. It fits into the
// architecture by [architectural role].
//
// Design Decisions:
// - [Key decision and why it was made]
// - [Another decision and rationale]
// =============================================================================Major code blocks (methods, complex logic, important sections) should have explanatory comments:
// Validates input parameters before processing.
// We validate early to fail fast and provide clear error messages,
// rather than letting errors propagate through the system.
private void ValidateInput(string path)
{
// ...
}
// Process items in batches to avoid memory pressure.
// Batch size of 100 was chosen based on benchmarking showing optimal
// throughput vs memory usage tradeoff.
foreach (var batch in items.Chunk(100))
{
// ...
}IMPORTANT: All public methods, properties, and classes must have /// XML documentation comments:
/// <summary>
/// Brief description of what the property represents.
/// </summary>
public int TotalUsers { get; set; }
/// <summary>
/// Brief description of what the method does.
/// </summary>
/// <param name="configuration">Description of the parameter.</param>
/// <returns>Description of the return value.</returns>
public DotnetPublishOptions WithConfiguration(Configuration configuration)
/// <summary>
/// Response containing list of projects.
/// </summary>
/// <param name="Projects">List of projects owned by the current user.</param>
public record GetProjectsResponse(IReadOnlyList<ProjectListItemDto> Projects);
/// <inheritdoc />
public async Task<Project?> GetProjectAsync(int projectId)- Use
//for single-line and short multi-line comments - Use
///XML doc comments for public API documentation (methods, properties, classes) - Use
<inheritdoc />on interface implementations - Explain the "why" not just the "what"
- Document design decisions and tradeoffs
- Comment on non-obvious logic or edge case handling
- Keep comments up to date when code changes
Use this command to find C# files missing the required header summary:
# Find .cs files without the header pattern (excludes obj/bin directories)
find src tests -name "*.cs" -not -path "*/obj/*" -not -path "*/bin/*" | xargs grep -L "^// =\+$" 2>/dev/null