# ANDO - Build and Deploy Toolchain > ANDO is a typed build system where build scripts are written in C# and executed in Docker containers. ## Quick Start ``` dotnet tool install -g ando echo 'Log.Info("Hello, World!");' > build.csando ando run ``` **Runtime Compatibility:** ANDO targets .NET 9. For the most predictable results, use a .NET 9 SDK image in your build container (e.g., `mcr.microsoft.com/dotnet/sdk:9.0`) unless you have a specific reason to deviate. ## LLM Guardrails (Read This Before Writing build.csando) ANDO build scripts are compiled as C# scripts with a fixed, typed API surface. If you generate scripts with an LLM, follow these rules to avoid the most common hallucinations: - Do NOT `using Ando.Core`, `using Ando.Dotnet`, `using Ando.Docker`, etc. Those namespaces are not part of the build-script surface. Use the globals documented below. - Do NOT call `UseImage(...)`, `CopyArtifactsToHost(...)`, or `CopyZippedArtifactsToHost(...)` as global functions. They are methods on the `Ando` global: `Ando.UseImage(...)`, etc. - `Env(name, required: bool)` has a second parameter named `required:` and it is a `bool`. There is no `Env(name, "default")` overload. Use `Env(..., required: false) ?? "default"` for defaults. - `Dotnet.Build(...)` and `Dotnet.Test(...)` use simple option properties (not fluent methods). Example: `o.Configuration = Configuration.Release; o.NoRestore = true;` - Docker tags use `WithTag(...)` (call it multiple times). There is no `WithTags(...)`. ### Minimal Verified Example (Compiles) ```csharp // ando-doc-snippet-test // build.csando var publish = DefineProfile("publish"); Ando.UseImage("mcr.microsoft.com/dotnet/sdk:9.0"); var app = Dotnet.Project("./src/App/App.csproj"); var tests = Dotnet.Project("./tests/App.Tests/App.Tests.csproj"); Dotnet.Restore(app); Dotnet.Build(app, o => { o.Configuration = Configuration.Release; o.NoRestore = true; }); Dotnet.Test(tests, o => { o.Configuration = Configuration.Release; o.NoRestore = true; o.NoBuild = true; }); if (publish) { Dotnet.Publish(app, o => o .WithConfiguration(Configuration.Release) .SkipRestore() .SkipBuild() .Output(Root / "artifacts" / "publish")); Docker.Build("Dockerfile", o => o .WithContext(".") .WithTag("myapp:local")); Ando.CopyZippedArtifactsToHost("artifacts", "./artifacts"); } ``` ## Prerequisites ANDO requires Docker to run builds in isolated containers. If Docker is not available, ANDO shows specific error messages: - Docker not installed: Shows installation instructions for your platform. - Docker daemon not running: Shows platform-specific instructions to start Docker Desktop (Windows/macOS) or the Docker daemon (Linux). ## CLI Commands - `ando` or `ando run` - Run the build script in a Docker container - `ando commit` - Commit all changes with AI-generated message using Claude - `ando bump [patch|minor|major]` - Bump version across all projects in build.csando (default: patch) - `ando docs` - Update documentation using Claude (syncs version badges in docs files, then analyzes changes and updates relevant docs) - `ando release` - Interactive release workflow (commit, docs, bump, push, publish). Steps are contextually enabled: commit skipped if no uncommitted changes, bump skipped if no changes since last tag, push skipped if no remote tracking. - `ando release --all` - Run all applicable release steps non-interactively - `ando release --dry-run` - Preview what release would do - `ando ship` - Ship workflow (commit, bump, docs, push) without the publish step. Same as release but skips building/publishing artifacts. - `ando ship --all` - Run all applicable ship steps non-interactively - `ando ship --dry-run` - Preview what ship would do - `ando verify` - Check build script for errors without executing - `ando clean` - Remove artifacts, temp files, and containers (removing container clears caches) - `ando help` - Show available commands - `ando --version` or `ando -v` - Show version number Unknown commands show an error with the help text (e.g., `ando foo` displays "Unknown command: foo" and lists available commands). ### Cancellation (Ctrl+C) - First Ctrl+C requests graceful cancellation (current step finishes, then build stops) - Second Ctrl+C forces immediate exit (exit code 130) ### Run Options - `-f, --file ` - Use a specific build file instead of build.csando - `-p, --profile ` - Activate build profiles (comma-separated, e.g., `-p publish,release`) - `--read-env` - Load environment file without prompting (checks for `.env.ando` first, falls back to `.env`) - `--verbosity ` - Set output verbosity (quiet|minimal|normal|detailed) - `--no-color` - Disable colored output - `--cold` - Always create a fresh container (ignore warm cache) - `--image ` - Use a custom Docker image - `--dind` - Mount Docker socket for Docker-in-Docker builds ### Docker-in-Docker (DIND) Detection ANDO automatically detects when your build script uses operations requiring DIND (Docker.Build, Docker.Push, Docker.Install, GitHub.PushImage, Playwright.Test). This detection also scans child builds invoked via `Ando.Build()`. If DIND is needed but not enabled, ANDO prompts: - (Y)es - Enable DIND for this run only - (A)lways - Enable DIND and save to ando.config - Esc - Cancel the build To skip the prompt, use `--dind` flag, add `dind: true` to `ando.config`, or set `ANDO_DIND=1` environment variable. Child builds automatically inherit DIND mode from parent builds (passed via both container and host process environment). **Non-interactive warning (CI):** If stdin is not interactive (CI, redirected output, some servers), the DIND prompt cannot be answered. In CI, always enable DIND explicitly via `ando run --dind ...`, `dind: true` in `ando.config`, or `ANDO_DIND=1`. **CI Server:** When running on the ANDO CI Server, the Docker CLI is automatically installed in DIND build containers. `Docker.Install()` is not required in build scripts on the server, but is recommended for portability. The server also configures a git committer identity (`user.name`/`user.email`) in build containers automatically, so `Git.Tag()` with annotated tags works without manual setup. Default: `Ando Server `. Override with `GIT_COMMITTER_NAME`/`GIT_COMMITTER_EMAIL` env vars. ### Claude Integration Several ANDO commands use Claude CLI for AI-powered features: - `ando commit` - Generates commit messages from diffs - `ando bump` - Generates changelog entries and updates changelog files - `ando docs` - Reviews commits since last tag and updates documentation Requirements: Claude CLI must be installed (`npm install -g @anthropic-ai/claude-code`) ### Docs Command Details Before running Claude, `ando docs` automatically syncs version badges in `website/src/pages/index.astro` and `README.md` to the current project version. This is non-fatal — failures are logged as warnings. The `ando docs` command searches for and updates these files: | Pattern | Location | Description | |---------|----------|-------------| | `*.md` | Anywhere | Markdown documentation files | | `*.astro` | `website/` | Astro page components | | `*.js` | `website/` | JavaScript data files | | `llms.txt` | `public/` | LLM-friendly documentation | Files skipped: - `CHANGELOG.md` - Handled by `ando bump` - Internal refactoring - Only user-facing changes What triggers updates: - New features, operations, or commands - Changed behavior affecting users - New options, parameters, or configuration - Examples that need updating Permissions: Claude runs with `--dangerously-skip-permissions` to allow file edits without interactive prompts. On first use, ANDO prompts: - (Y)es - Allow Claude for this run only - (n)o - Cancel the command - (A)lways - Allow Claude and save `allowClaude: true` to ando.config To skip the prompt, add `allowClaude: true` to your `ando.config` file. ### Configuration File ANDO supports an optional `ando.config` file (YAML format) in the project root: ```yaml # ando.config dind: true readEnv: true allowClaude: true ``` Settings: - `dind` - Enable Docker-in-Docker mode by default - `readEnv` - Automatically load environment files without prompting - `allowClaude` - Allow Claude CLI to run with elevated permissions without prompting ### Environment Files ANDO looks for `.env.ando` (preferred) or `.env` (fallback) in the project root. If `.env.ando` exists but is not in `.gitignore`, ANDO warns about potential secret exposure and prompts: - (A)dd to .gitignore - Appends `.env.ando` to `.gitignore` (default) - (C)ontinue anyway - Proceed without modifying `.gitignore` - Esc - Abort the build When environment variables are found, ANDO prompts: - (Y)es - Load for this run only - (n)o - Skip loading - for this (r)un - Load for this run and all sub-builds - (a)lways - Load and save `readEnv: true` to ando.config Use `--read-env` or set `readEnv: true` in `ando.config` to skip the prompt and load automatically. ### Project Files ANDO uses and creates these files/directories: | Path | Description | |------|-------------| | `build.csando` | Build script (C# with Roslyn scripting) | | `ando.config` | YAML config file for persistent settings | | `.env.ando` | Environment variables (preferred, project-specific) | | `.env` | Environment variables (fallback) | | `build.csando.log` | Plain-text log of last build run (overwritten each run) | | `.ando/cache/` | Package caches persist inside warm containers (not on host) | | `.ando/tmp/` | Temporary files used during builds | | `artifacts/` | Default output directory for build artifacts | ### Logging ANDO writes a plain-text log to `build.csando.log` in the project root. This file is overwritten on each build run (not appended) and contains the same output as the console without ANSI color codes. ### Suggested .gitignore ```gitignore # ANDO build system .env.ando build.csando.log .ando/ artifacts/ ``` Explanation: - `.env.ando` - Contains secrets (API keys, tokens). ANDO warns if not gitignored. - `build.csando.log` - Build output log, regenerated on each run. - `.ando/` - Cache directories (NuGet, npm) and temp files. Large and machine-specific. - `artifacts/` - Build outputs. Should be regenerated, not committed. ### Hooks (Pre-Build and Post-Build) ANDO supports pre-build and post-build hooks - `.csando` scripts that run automatically before and after CLI commands. Hooks execute on the host machine (not in Docker). **Hook Types:** | Hook | When It Runs | |------|--------------| | `ando-pre.csando` | Before **every** command | | `ando-post.csando` | After **every** command | | `ando-pre-{cmd}.csando` | Before a **specific** command | | `ando-post-{cmd}.csando` | After a **specific** command | **File Locations (first found wins):** 1. `./scripts/ando-{hook}.csando` 2. `./ando-{hook}.csando` **Hook API:** | Global | Description | |--------|-------------| | `Root` | Project root path (supports `/` operator) | | `Log` | Logging: `Log.Info()`, `Log.Warning()`, `Log.Error()` | | `Shell` | Execute commands (hooks only): `await Shell.RunAsync("cmd", "args")` | | `Env(name)` | Get environment variable | | `Directory(path)` | Create directory reference | **Important:** `Shell` is a hooks-only API. It is not a global in `build.csando` build scripts. **Environment Variables:** | Variable | Available In | Description | |----------|--------------|-------------| | `ANDO_COMMAND` | All hooks | Command being executed | | `ANDO_EXIT_CODE` | Post-hooks only | Exit code (`0` = success) | | `ANDO_OLD_VERSION` | bump hooks | Version before bump | | `ANDO_NEW_VERSION` | post-bump | Version after bump | | `ANDO_BUMP_TYPE` | bump hooks | patch, minor, or major | **Behavior:** - Pre-hook failure aborts the command - Post-hook failure only warns (command already completed) - For `release` and `ship` commands, post-hooks only run when the command succeeds (exit code 0) **Example pre-hook (validate before bump):** ```csharp // scripts/ando-pre-bump.csando var result = await Shell.RunAsync("dotnet", "test", "--no-build"); if (result.ExitCode != 0) throw new Exception("Tests must pass"); ``` **Example post-hook (auto-update after release):** ```csharp // scripts/ando-post-release.csando // Skip if release failed var exitCode = Env("ANDO_EXIT_CODE", required: false); if (exitCode != "0") return; await Shell.RunAsync("dotnet", "tool", "update", "-g", "mytool"); Log.Info("Tool updated!"); ``` Full documentation: https://andobuild.com/hooks ## Key Facts - File: `build.csando` (C# code, no .cs extension) - Execution: All builds run in Docker containers - No `using` statements needed - namespaces are pre-imported - Operations register steps that execute in order ## Scripting Basics Build scripts are C# code executed via Roslyn scripting. Everything in the script runs at the top level (no class or method wrappers needed). The following are available as **globals** — no prefix or import required. ### Global Functions (NOT on the Ando object) These are standalone functions, NOT methods on the `Ando` object: ```csharp // CORRECT - these are global functions: var dir = Directory("./frontend"); var key = Env("API_KEY"); var release = DefineProfile("release"); // WRONG - these are NOT on the Ando object: // var dir = Ando.Directory("./frontend"); // ERROR // var key = Ando.Env("API_KEY"); // ERROR ``` ### Environment Variables (Env) The `Env()` function gets environment variables. Its signature is: ``` string? Env(string name, bool required = true) ``` Three usage patterns: ```csharp // 1. Required (default) - throws if not set var apiKey = Env("API_KEY"); // 2. Optional - returns null if not set var optional = Env("OPTIONAL_VAR", required: false); // 3. Optional with default - use null-coalescing var lang = Env("SITE_LANG", required: false) ?? "en"; ``` **Important:** The second parameter is `required:` (a bool), NOT a default value. `Env("NAME", "default")` will NOT compile — use `Env("NAME", required: false) ?? "default"` instead. There is no `SetEnv()` function. Environment variables must be set externally via `.env.ando` files, the OS environment, or CI/CD configuration. ### Two Path Types: BuildPath vs DirectoryRef ANDO has two path types that serve different purposes: | Type | Created By | Used For | |------|-----------|----------| | `BuildPath` | `Root`, `Temp`, path `/` operator | Path construction and string arguments | | `DirectoryRef` | `Directory()` function | Operations that need a working directory (Npm, Cloudflare, Playwright, Ando.Build) | ```csharp // BuildPath - for path construction and string arguments var outputPath = Root / "dist"; // BuildPath Dotnet.Publish(app, o => o.Output(outputPath)); // OK: accepts string/BuildPath // DirectoryRef - for operations that need a working directory var frontend = Directory("./frontend"); // DirectoryRef Npm.Ci(frontend); // OK: requires DirectoryRef // WRONG: Root is BuildPath, not DirectoryRef // Npm.Ci(Root); // ERROR: type mismatch // Use Directory(".") instead: Npm.Ci(Directory(".")); // OK ``` `DirectoryRef` also supports the `/` operator: ```csharp var site = Directory("./frontend"); Cloudflare.PagesDeploy(site / "dist", "my-project"); // DirectoryRef / string = DirectoryRef ``` ### Profiles Profiles allow conditional execution. `DefineProfile()` takes a single argument (the name) and returns a `Profile` object that you **must capture in a variable**. The `Profile` object has implicit `bool` conversion — it evaluates to `true` when that profile is active (activated via `-p` CLI flag): ```csharp // Define at the top of your script — capture the return value var release = DefineProfile("release"); // Returns Profile object var publish = DefineProfile("publish"); // The Profile variable evaluates to true/false in if statements if (release) { Git.Tag("v1.0.0"); // Only runs when: ando -p release } if (publish) { Nuget.Push(app); // Only runs when: ando -p publish } // Activate via CLI: // ando -p release // ando -p publish,release (multiple profiles) ``` **Note:** There is no `HasProfile()`, `HasAnyProfile()`, or `Profile("name")` function. The only way to define and check profiles is `DefineProfile()` + `if (profile)`. ## Global Variables ### Path Globals | Variable | Type | Description | |----------|------|-------------| | Root | BuildPath | Project root directory (where build.csando is). Supports `/` operator for path joining. Use for path construction, NOT for operations requiring DirectoryRef. | | Temp | BuildPath | Temporary files directory (root/.ando/tmp). Supports `/` operator for path joining. | ### Operation Objects | Variable | Type | Description | |----------|------|-------------| | Dotnet | DotnetOperations | .NET CLI operations (build, test, publish). | | Ef | EfOperations | Entity Framework Core operations. | | Npm | NpmOperations | npm operations (install, run, ci). Requires DirectoryRef arguments. | | Node | NodeInstallOperations | Node.js installation. | | Azure | AzureOperations | Azure CLI authentication. | | Bicep | BicepOperations | Azure Bicep deployments. | | AppService | AppServiceOperations | Azure App Service deployments. | | Functions | FunctionsOperations | Azure Functions deployments. | | Cloudflare | CloudflareOperations | Cloudflare Pages deployments. Requires DirectoryRef arguments. | | Nuget | NugetOperations | NuGet package operations (pack, push). | | Docker | DockerOperations | Docker image building. | | Docfx | DocfxOperations | DocFX API documentation generation. | | Git | GitOperations | Git version control operations (tag, push). | | GitHub | GitHubOperations | GitHub releases, PRs, and container registry. | | Ando | AndoOperations | Build configuration, artifacts, and nested builds. | | Log | LogOperations | Logging (Info, Warning, Error, Debug). | | Playwright | PlaywrightOperations | Playwright E2E testing. Requires DirectoryRef arguments. | ## Global Functions | Function | Signature | Returns | Description | |----------|-----------|---------|-------------| | Directory | Directory(path = ".") | DirectoryRef | Create a directory reference for use with operations like Npm, Cloudflare, Playwright, and Ando.Build. | | Env | Env(name, required = true) | string? | Get environment variable. Throws if not set by default. Use `Env("NAME", required: false) ?? "default"` for optional with default. | | DefineProfile | DefineProfile(name) | Profile | Define a build profile. Returns Profile with implicit bool conversion. Activate with `-p name`. | ## Common Patterns ### Path Operations ```csharp var outputPath = Root / "artifacts" / "publish"; var distDir = Directory("./frontend") / "dist"; ``` ### .NET Build + Test + Publish ```csharp var project = Dotnet.Project("./src/MyApp/MyApp.csproj"); Log.Info($"Building {project.Name} version {project.Version}"); Dotnet.Restore(project); Dotnet.Build(project); Dotnet.Test(project); Dotnet.Publish(project, o => o.Output(Root / "artifacts")); ``` ### Frontend Build ```csharp var frontend = Directory("./frontend"); Node.Install("20"); Npm.Ci(frontend); Npm.Run(frontend, "build"); ``` ### Azure Deployment ```csharp Azure.EnsureAuthenticated(); Azure.SetSubscription("my-subscription-id"); AppService.DeployZip("my-app", Root / "artifacts" / "app.zip"); ``` ### Cloudflare Pages ```csharp Cloudflare.EnsureAuthenticated(); Cloudflare.PagesDeploy(Directory("./website") / "dist", "my-project"); ``` ### Copy Artifacts to Host ```csharp // Copy build outputs from container to host after build completes Ando.CopyArtifactsToHost("dist", "./dist"); // Copy as compressed archive (faster for many small files) Ando.CopyZippedArtifactsToHost("dist", "./output"); // Creates ./output/artifacts.tar.gz Ando.CopyZippedArtifactsToHost("dist", "./dist/binaries.tar.gz"); // Specific .tar.gz filename Ando.CopyZippedArtifactsToHost("dist", "./dist/binaries.zip"); // Zip format ``` ### Set Docker Image ```csharp // Use a specific Docker image for the build Ando.UseImage("mcr.microsoft.com/dotnet/sdk:9.0"); ``` ### GHCR Publish Template (Copy/Paste) ```csharp // Publish a multi-arch Docker image to GitHub Container Registry (GHCR). // Requirements: // - run with DIND enabled (CI: `ando run --dind ...`) // - `GITHUB_TOKEN` available with `write:packages` scope for GHCR pushes var version = Dotnet.Project("./src/MyApp/MyApp.csproj").Version; // Prefer GitHub Actions vars when present; fall back to a fixed owner/name. var owner = Env("GITHUB_REPOSITORY_OWNER", required: false) ?? "my-org"; var imageName = "my-app"; Docker.Install(); Docker.Build("./Dockerfile", o => o .WithPlatforms("linux/amd64", "linux/arm64") .WithTag($"ghcr.io/{owner}/{imageName}:{version}") .WithTag($"ghcr.io/{owner}/{imageName}:latest") .WithContext(".") .WithPush()); ``` ### Nested Builds ```csharp // Run a child build in a subdirectory (own container, own .env.ando or .env) Ando.Build(Directory("./website")); // Run a specific build file Ando.Build(Directory("./website") / "deploy.csando"); // With options Ando.Build(Directory("./api"), o => o.WithDind().ColdStart()); ``` ### Full Example: .NET Tool with NuGet Publishing ```csharp // build.csando - Build .NET tool with profiles for conditional publishing // Usage: // ando # Build and test only // ando -p publish # Build, test, and push to NuGet.org + deploy docs var publish = DefineProfile("publish"); var project = Dotnet.Project("./src/MyTool/MyTool.csproj"); var testProject = Dotnet.Project("./tests/MyTool.Tests/MyTool.Tests.csproj"); var distPath = Root / "dist"; // Install .NET SDK Dotnet.SdkInstall(); // Restore dependencies Dotnet.Restore(project); // Build the project Dotnet.Build(project); // Run tests Dotnet.Test(testProject); // Publish for multiple platforms var runtimes = new[] { "win-x64", "linux-x64", "osx-x64", "osx-arm64" }; foreach (var runtime in runtimes) { Log.Info($"Publishing for {runtime}..."); Dotnet.Publish(project, o => o .WithRuntime(runtime) .Output(distPath / runtime) .AsSelfContained() .AsSingleFile()); } // Create NuGet package for the dotnet tool Nuget.Pack(project); // Push to NuGet.org (only with -p publish) if (publish) { Nuget.EnsureAuthenticated(); Nuget.Push(project); // Build and deploy the documentation website Ando.Build(Directory("./website")); } // Copy the dist folder from container to host Ando.CopyArtifactsToHost("dist", "./dist"); Ando.CopyZippedArtifactsToHost("dist", "./dist/binaries.zip"); ``` ## All Operations ### Ando - Directory(path?) - Creates a reference to a directory - Root - The root path of the project (where build.csando is located) - Temp - Temporary files directory (root/.ando/tmp) - Env(name, required?) - Get environment variable. Throws if not set by default. Pass required: false to return null. - Ando.UseImage(image) - Set the Docker image for the build container - Ando.CopyArtifactsToHost(containerPath, hostPath) - Copy files from container to host after build - Ando.CopyZippedArtifactsToHost(containerPath, hostPath) - Create archive and copy from container to host. Supports .tar.gz (default) and .zip. If hostPath is a directory, creates artifacts.tar.gz. - Ando.Build(directory, options?) - Run a nested build in a subdirectory (new container, isolated context) - Log.Info(message) - Log informational message - Log.Warning(message) - Log warning message - Log.Error(message) - Log error message - Log.Debug(message) - Log debug message ### Node - Node.Install(version?) - Install Node.js globally (default: v22) ### Dotnet - Dotnet.SdkInstall(version?) - Install .NET SDK globally (default: 9.0). Use for base images without .NET. - Dotnet.Project(path) - Create a .NET project reference from a .csproj path. Returns ProjectRef with Path, Name, Directory, and Version properties. - Dotnet.Restore(project) - Restore NuGet packages - Dotnet.Build(project) - Compile a project - Dotnet.Test(project) - Run unit tests - Dotnet.Publish(project, options?) - Create deployment artifacts - Dotnet.Tool(name, version?) - Reference a .NET CLI tool ### Ef - Ef.DbContextFrom(project, contextName?) - Reference a DbContext - Ef.DatabaseUpdate(context, connectionString?) - Apply pending migrations - Ef.DatabaseUpdate(context, outputRef) - Apply migrations with connection from deployment output - Ef.Script(context, outputPath) - Generate SQL migration script ### Npm - Npm.Install(directory) - Run npm install - Npm.Ci(directory) - Run npm ci (clean install) - Npm.Run(directory, script) - Run an npm script - Npm.Test(directory) - Run npm test - Npm.Build(directory) - Run npm run build ### Azure - Azure.EnsureAuthenticated() - Authenticate using best available method - Azure.EnsureLoggedIn() - Verify logged in to Azure CLI - Azure.ShowAccount() - Display current account info - Azure.LoginWithServicePrincipal(clientId?, secret?, tenantId?) - Login with SP - Azure.LoginWithManagedIdentity(clientId?) - Login with managed identity - Azure.SetSubscription(subscriptionId?) - Set active subscription - Azure.CreateResourceGroup(name, location) - Create resource group ### Bicep - Bicep.DeployToResourceGroup(rg, template, options?) - Deploy to resource group, returns BicepDeployment - Bicep.DeployToSubscription(location, template, options?) - Deploy at subscription scope, returns BicepDeployment - Bicep.WhatIf(rg, template, options?) - Preview deployment - Bicep.Build(template, output?) - Compile Bicep to ARM JSON BicepDeployment objects provide typed access to deployment outputs: - deployment.Output("name") - Returns OutputRef that resolves at execution time - deployment.GetOutput("name") - Gets output value directly (call during step execution) ### Cloudflare - Cloudflare.EnsureAuthenticated() - Verify Cloudflare credentials - Cloudflare.PagesDeploy(directory, projectName, options?) - Deploy to Pages - Cloudflare.PagesListProjects() - List all Pages projects - Cloudflare.PagesCreateProject(name, branch?) - Create a Pages project - Cloudflare.PagesListDeployments(projectName?) - List deployments - Cloudflare.PurgeCache(zoneIdOrDomain?) - Purge entire cache ### Functions - Functions.DeployZip(name, zipPath, rg?, options?) - Deploy via zip deploy - Functions.Publish(name, projectPath?, options?) - Publish using func CLI - Functions.DeployWithSwap(name, zipPath, slot?, rg?) - Deploy then swap - Functions.SwapSlots(name, sourceSlot, rg?, targetSlot?) - Swap slots - Functions.Restart(name, rg?, slot?) - Restart function app - Functions.Start(name, rg?, slot?) - Start function app - Functions.Stop(name, rg?, slot?) - Stop function app ### AppService - AppService.DeployZip(name, zipPath, rg?, options?) - Deploy via zip deploy - AppService.DeployWithSwap(name, zipPath, slot?, rg?) - Deploy then swap - AppService.SwapSlots(name, sourceSlot, rg?, targetSlot?) - Swap slots - AppService.CreateSlot(name, slotName, rg?, configSource?) - Create slot - AppService.DeleteSlot(name, slotName, rg?) - Delete slot - AppService.ListSlots(name, rg?) - List deployment slots - AppService.Restart(name, rg?, slot?) - Restart app service - AppService.Start(name, rg?, slot?) - Start app service - AppService.Stop(name, rg?, slot?) - Stop app service ### Nuget - Nuget.EnsureAuthenticated() - Ensure API key is available (prompts if NUGET_API_KEY not set) - Nuget.Pack(project, options?) - Create NuGet package (default: Release config, outputs to bin/Release) - Nuget.Push(project, options?) - Push packages from bin/Release/*.nupkg (default: nuget.org, skips duplicates) - Nuget.Push(path, options?) - Push package from path/glob (default: nuget.org, skips duplicates) ### Docker - Docker.Install() - Install Docker CLI in container (required for Docker.Build with --dind). Automatically handled on the CI server; recommended in scripts for portability. - Docker.IsAvailable() - Check if Docker CLI and daemon are accessible (returns bool immediately, not a step) - Docker.Build(dockerfile, options?) - Build Docker image using buildx. Supports single or multi-platform builds with optional push to registry. **Recommended**: Use WithPush() for atomic build+push to ensure both version and latest tags point to the same manifest. Auto-handles ghcr.io auth when pushing. #### GHCR Publishing Rules (Read This First) **Pushes only happen when BOTH are true:** 1. You call `WithPush()` 2. At least one `WithTag(...)` includes a registry hostname (e.g., `ghcr.io/org/app:tag`) Truth table: | Build Script | Result | |------------|--------| | `WithPush()` + tag contains `ghcr.io/...` | Pushes to GHCR (and ANDO auto-auths) | | No `WithPush()` | Builds locally only (nothing is pushed anywhere) | | `WithPush()` + tags without registry hostname | Docker may attempt Docker Hub or fail; usually unintended | #### GHCR Auth (Do This, Not That) - Do not run `docker login` yourself in `build.csando`. Use `WithTag("ghcr.io/...")` + `WithPush()`. ANDO handles auth for GHCR pushes. - Token sources (in order): - `GITHUB_TOKEN` environment variable (preferred) - `gh auth token` (requires `gh` CLI to be installed and authenticated) - Note: `gh` itself also supports `GH_TOKEN`, but ANDO's primary mechanism is `GITHUB_TOKEN`. ### Docfx - Docfx.Install() - Install DocFX as a dotnet global tool (skips if already installed) - Docfx.IsInstalled() - Check if DocFX is installed (returns bool immediately, not a step) - Docfx.GenerateDocs(configPath?) - Generate API documentation from docfx.json (runs metadata + build) - Docfx.CopyToDirectory(sourceDir, targetDir) - Copy generated docs to target directory - Docfx.BuildAndCopy(configPath, outputDir, targetDir) - Generate docs, copy, and cleanup in one step - Docfx.Cleanup(outputDir?) - Remove intermediate DocFX files (api/ and output directories) ### Playwright - Playwright.Install(directory) - Install browsers AND system dependencies (runs `npx playwright install --with-deps`) - Playwright.Test(directory, options?) - Run Playwright tests (default: `npx playwright test`) PlaywrightTestOptions: - Project - Run specific project (e.g., "chromium") - Headed - Run with visible browser - UI - Run in interactive UI mode - Workers - Number of parallel workers - Reporter - Reporter to use (e.g., "html", "list") - Grep - Filter tests by pattern - UpdateSnapshots - Update visual snapshots - UseNpmScript - Use `npm run` instead of `npx playwright test` - NpmScriptName - Custom script name when UseNpmScript is true ### Git - Git.Tag(tagName, options?) - Create a git tag (annotated by default) - Git.Push(options?) - Push current branch to remote - Git.PushTags(remote?) - Push tags to remote, skipping tags that already exist remotely (default: origin). Tags are pushed individually. ### GitHub - GitHub.CreatePr(options) - Create a pull request (requires gh CLI) - GitHub.CreateRelease(options) - Create a GitHub release with optional file uploads (use WithFiles() to attach assets, supports path#name syntax) - GitHub.PushImage(imageName, options?) - **Deprecated**: Use Docker.Build with WithPush() instead for atomic builds. ### Profiles Profiles allow conditional execution of build steps. `DefineProfile()` returns a `Profile` object — capture it in a variable. It evaluates to `true` when that profile is active: ```csharp // Capture the Profile in a variable var release = DefineProfile("release"); Dotnet.Build(app); Dotnet.Test(tests); // Profile evaluates to true when activated via -p flag if (release) { Git.Tag("v1.0.0"); GitHub.CreateRelease(o => o .WithTag("v1.0.0") .WithGeneratedNotes() .WithFiles("dist/app#app-linux", "dist/app.exe#app-windows.exe")); } ``` Activate with CLI: `ando -p release` or `ando -p publish,release` ## Options Reference ### Dotnet.Restore Options - WithRuntime(string) - Target runtime identifier (e.g., "linux-x64", "win-x64") - NoCache - Bypass NuGet cache, force fresh downloads ### Dotnet.Build Options - Configuration - Debug or Release - NoRestore - Skip implicit restore ### Dotnet.Test Options - Configuration - Build configuration - NoRestore - Skip restore - NoBuild - Skip build (implies NoRestore) - Filter - Test filter expression (e.g., "Category=Unit", "Name=MyTest") ### Dotnet.Publish Options - Output(path) - Output directory for artifacts - WithConfiguration(Configuration) - Debug or Release - WithRuntime(string) - Target runtime (e.g., "linux-x64", "linux-arm64", "win-x64", "osx-arm64") - AsSelfContained(bool) - Include .NET runtime (default: true when called) - AsSingleFile(bool) - Bundle into single executable - SkipRestore() - Skip restore before publishing - SkipBuild() - Skip build before publishing ### Nuget.Pack Options - Output(path) - Output directory for .nupkg - WithConfiguration(Configuration) - Build configuration - WithVersion(string) - Explicit package version - WithVersionSuffix(string) - Pre-release suffix (e.g., "beta", "rc1") - WithSymbols(bool) - Generate .snupkg for debugging - WithSource(bool) - Include source files in symbols - SkipRestore() - Skip restore - SkipBuild() - Skip build ### Nuget.Push Options - ToSource(string) - NuGet feed URL - ToNuGetOrg() - Push to nuget.org (default) - WithApiKey(string) - API key (or use NUGET_API_KEY env var) - SkipDuplicates(bool) - Skip if version exists (default: true) - WithoutSymbols(bool) - Don't push .snupkg ### Docker.Build Options - WithTag(string) - Add image tag (can call multiple times). Use full registry path for push (e.g., "ghcr.io/org/app:v1") - WithPlatform(string) - Single target platform (e.g., "linux/amd64") - WithPlatforms(params string[]) - Multiple target platforms for multi-arch builds (e.g., "linux/amd64", "linux/arm64") - WithContext(string) - Build context directory - WithBuildArg(key, value) - Pass build argument to Dockerfile - WithPush() - **Recommended**: Push images to registry atomically during build. Ensures all tags point to same manifest. - WithNoCache() - Disable build cache - WithoutLoad() - Disable loading image into local docker (useful when only pushing) ### Git.Tag Options - WithMessage(string) - Tag message for annotated tags - AsLightweight() - Create lightweight tag instead of annotated - WithSkipIfExists() - Skip if tag exists (warn instead of fail) Note: Annotated tags require a git identity (user.name/user.email). The ANDO CI Server configures this automatically. For CLI builds, the host git config is used. ### Git.Push Options - ToRemote(string) - Remote name (default: origin) - ToBranch(string) - Branch to push - WithUpstream() - Set upstream tracking (-u flag) ### GitHub.CreateRelease Options - WithTag(string) - Tag name (e.g., "v1.0.0") - WithTitle(string) - Release title - WithNotes(string) - Markdown release notes - WithGeneratedNotes() - Auto-generate notes from commits since last release - AsDraft() - Create as draft (not visible until published) - AsPrerelease() - Mark as pre-release - WithoutPrefix() - Don't add 'v' prefix to tag - WithFiles(params string[]) - Files to upload as release assets ### GitHub.CreatePr Options - WithTitle(string) - PR title - WithBody(string) - PR description (markdown) - WithBase(string) - Target branch (e.g., "main") - WithHead(string) - Source branch - AsDraft() - Create as draft PR ### Bicep.Deploy Options - WithName(string) - Custom deployment name - WithParameterFile(string) - Path to .json or .bicepparam file - WithParameter(name, value) - Inline parameter - WithMode(DeploymentMode) - Incremental (default) or Complete - WithDeploymentSlot(string) - App Service slot ### Cloudflare.PagesDeploy Options - WithProjectName(string) - Pages project name - WithBranch(string) - Git branch for deployment - WithCommitHash(string) - Commit hash for tracking - WithCommitMessage(string) - Commit message for tracking ### AppService.DeployZip Options - WithDeploymentSlot(string) - Deploy to specific slot - WithNoWait() - Don't wait for completion - WithRestart() - Restart after deployment ### Functions.DeployZip / Publish Options - WithDeploymentSlot(string) - Deploy to specific slot - WithConfiguration(string) - Build configuration - WithForceRestart() - Force restart after deployment - WithNoWait() - Don't wait for completion ### Ando.Build Options - WithVerbosity(string) - Output verbosity (Quiet, Minimal, Normal, Detailed) - ColdStart(bool) - Force fresh container - WithDind(bool) - Enable Docker-in-Docker - WithImage(string) - Override Docker image - WithProfile(string) - Activate profiles for child build (e.g., "publish" or "test,release") ## Tips for Writing Build Scripts 1. Build scripts are C# - use C# syntax including var, lambdas, string interpolation 2. No semicolons needed at end of lines (but they work if included) 3. Operations execute in the order they are called 4. Use Dotnet.Project() for .csproj files, Directory() for folders 5. The `/` operator concatenates paths: Root / "src" / "app" 6. Options are configured via fluent lambdas: o => o.Output(...).WithRuntime(...) 7. Always call EnsureAuthenticated() before cloud operations 8. Use `ando verify` to check scripts without executing ## Common Mistakes | Mistake | Error | Fix | |---------|-------|-----| | `Ando.Env("NAME")` | CS1061: 'AndoOperations' does not contain 'Env' | Use `Env("NAME")` — it's a global function | | `Env("NAME", "default")` | CS1503: cannot convert from 'string' to 'bool' | Use `Env("NAME", required: false) ?? "default"` | | `DefineProfile("name", "desc")` | CS1501: No overload takes 2 arguments | Use `DefineProfile("name")` — single argument only | | `HasProfile("name")` | CS0103: 'HasProfile' does not exist | Use `var p = DefineProfile("name"); if (p) { ... }` | | `Ando.SetEnv("NAME", "value")` | CS1061: 'AndoOperations' does not contain 'SetEnv' | Set env vars via `.env.ando` files or OS environment | | `Npm.Ci(Root)` | CS1503: cannot convert 'BuildPath' to 'DirectoryRef' | Use `Npm.Ci(Directory("."))` | | `Profile("name")` | CS0119: 'Profile' is a type | Use `DefineProfile("name")` to create profiles | ## API Documentation For detailed C# API documentation including all classes, methods, properties, and XML doc comments, see the API reference: - https://andobuild.com/apidocs/api/Ando.html - Main Ando namespace (CLI, Operations, Workflow) - https://andobuild.com/apidocs/api/Ando.Operations.html - All operation classes (DotnetOperations, DockerOperations, etc.) - https://andobuild.com/apidocs/api/Ando.Workflow.html - Workflow execution (WorkflowRunner, BuildStep, StepRegistry) - https://andobuild.com/apidocs/api/Ando.Context.html - Build context (BuildPath, PathsContext, VarsContext) - https://andobuild.com/apidocs/api/Ando.References.html - Project references (ProjectRef, DirectoryRef, EfContextRef) - https://andobuild.com/apidocs/api/Ando.Logging.html - Logging interfaces (IBuildLogger, ConsoleLogger) - https://andobuild.com/apidocs/api/Ando.Execution.html - Command execution (ProcessRunner, ContainerExecutor, DockerManager) - https://andobuild.com/apidocs/api/Ando.Scripting.html - Roslyn scripting (ScriptHost, ScriptGlobals, BuildOperations) - https://andobuild.com/apidocs/api/Ando.Profiles.html - Build profiles (ProfileRegistry, Profile) - https://andobuild.com/apidocs/api/Ando.Utilities.html - Utility classes (VersionResolver, GitHubAuthHelper) The API docs are generated from C# XML documentation comments using DocFX and provide complete type information, method signatures, and usage examples. ## Markdown Versions All pages are available in markdown format for LLM consumption: ### Main Pages - https://andobuild.com/index.md - Homepage and overview - https://andobuild.com/about.md - About ANDO - https://andobuild.com/cli.md - CLI reference - https://andobuild.com/hooks.md - Pre-build and post-build hooks - https://andobuild.com/llm-prompt.md - LLM prompt for adding ANDO to projects - https://andobuild.com/server.md - CI Server installation, configuration, and API tokens - https://andobuild.com/server-truenas.md - CI Server installation on TrueNAS SCALE - https://andobuild.com/changelog.md - Release history - https://andobuild.com/license.md - License information ### Provider Documentation - https://andobuild.com/providers/ando.md - Core operations (Log, Artifacts, Context) - https://andobuild.com/providers/appservice.md - Azure App Service deployments - https://andobuild.com/providers/azure.md - Azure CLI authentication - https://andobuild.com/providers/bicep.md - Azure Bicep deployments - https://andobuild.com/providers/cloudflare.md - Cloudflare Pages deployments - https://andobuild.com/providers/docfx.md - DocFX API documentation generation - https://andobuild.com/providers/docker.md - Docker image building - https://andobuild.com/providers/dotnet.md - .NET build, test, publish - https://andobuild.com/providers/ef.md - Entity Framework Core migrations - https://andobuild.com/providers/functions.md - Azure Functions deployments - https://andobuild.com/providers/git.md - Git version control operations - https://andobuild.com/providers/github.md - GitHub releases and container registry - https://andobuild.com/providers/node.md - Node.js installation - https://andobuild.com/providers/npm.md - npm operations - https://andobuild.com/providers/nuget.md - NuGet package operations - https://andobuild.com/providers/playwright.md - Playwright E2E testing ### Examples - https://andobuild.com/examples/index.md - All examples by category **.NET** - https://andobuild.com/examples/dotnet-tool.md - .NET CLI tool with NuGet publishing - https://andobuild.com/examples/ef-migrations.md - EF Core database migrations - https://andobuild.com/examples/git-versioning.md - Git tag versioning and semantic versioning - https://andobuild.com/examples/docfx-api-docs.md - DocFX API documentation generation - https://andobuild.com/examples/monorepo-build.md - Monorepo orchestration with Ando.Build - https://andobuild.com/examples/nuget-publishing.md - NuGet.org package publishing **Frontend** - https://andobuild.com/examples/astro.md - Astro + Cloudflare Pages - https://andobuild.com/examples/cloudflare-workers.md - Cloudflare Workers serverless deployment - https://andobuild.com/examples/react-spa.md - React/Vue SPA with Vite **Full Stack** - https://andobuild.com/examples/fullstack-deploy.md - .NET API + JS frontend deployment **Azure** - https://andobuild.com/examples/azure-app-service.md - Azure App Service deployment - https://andobuild.com/examples/azure-functions.md - Azure Functions deployment - https://andobuild.com/examples/bicep-infrastructure.md - Bicep infrastructure as code **Docker** - https://andobuild.com/examples/container-github-registry.md - Docker to GitHub Container Registry - https://andobuild.com/examples/docker-compose.md - Docker Compose multi-container apps **GitHub** - https://andobuild.com/examples/github-releases.md - GitHub releases with auto-generated notes - https://andobuild.com/examples/nuget-github-packages.md - NuGet to GitHub Packages **Testing** - https://andobuild.com/examples/playwright-e2e-tests.md - Playwright E2E tests **Hooks** - https://andobuild.com/examples/pre-hook-cleanup.md - Pre-build cleanup hook - https://andobuild.com/examples/post-release-hook.md - Post-release auto-update hook