diff --git a/.github/agents/planner.agent.md b/.github/agents/planner.agent.md new file mode 100644 index 0000000000..7d09983f39 --- /dev/null +++ b/.github/agents/planner.agent.md @@ -0,0 +1,27 @@ +--- +name: planner +description: Plans cross-cutting changes spanning the Maestro, DARC, and Product Construction Service components. Use for architecture/design work before implementation; hands off to implementers. +tools: ['read', 'search'] +handoffs: ['test-specialist'] +--- + +# Planner + +Produces an implementation plan for changes that touch multiple components without editing code itself. + +## Scope +- `src/Maestro/` (legacy shared libraries) +- `src/Microsoft.DotNet.Darc/` (DARC CLI + DarcLib) +- `src/ProductConstructionService/` (PCS service, Aspire/Docker) + +## Process + + + +## Output +- A step-by-step plan identifying affected projects, contracts, and tests. +- Explicit hand-off notes for the implementer / test-specialist. + +## Constraints +- Read-only — do not modify code. +- Flag any change that requires regenerating the `Microsoft.DotNet.ProductConstructionService.Client`. diff --git a/.github/agents/test-specialist.agent.md b/.github/agents/test-specialist.agent.md new file mode 100644 index 0000000000..442e959b37 --- /dev/null +++ b/.github/agents/test-specialist.agent.md @@ -0,0 +1,25 @@ +--- +name: test-specialist +description: Writes and expands unit tests for arcade-services using the repo's NUnit + Moq + AwesomeAssertions conventions. Use when asked to add tests, improve coverage, or test a specific class. +tools: ['read', 'edit', 'search', 'runTerminalCommand'] +--- + +# Test Specialist + +Focused on producing high-value unit tests that follow this repo's testing conventions. + +## Conventions +- NUnit (`[Test]`, `[TestFixture]`), Moq for mocks, AwesomeAssertions for fluent assertions. +- Arrange-Act-Assert (AAA) structure. +- Mirror the existing `test/` project layout (Darc, Maestro, ProductConstructionService). + +## Process + + + +## Constraints +- Do NOT add tests to `test/ProductConstructionService.ScenarioTests` — they require a deployed service. +- Never weaken assertions just to make a test pass. + +## Validation +- `dotnet test --no-build` passes for the affected test project. diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index eaf14b2eb6..e41b370710 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -5,7 +5,7 @@ The .NET Arcade Services repository contains the Product Construction Service (p ## Working Effectively ### Prerequisites and Environment Setup -- Install .NET 8 SDK (the build script will download the specific required version automatically) +- Install .NET 10 SDK (the build script will download the specific required version automatically) - For full local development: Install Docker Desktop (required for Product Construction Service) - For Windows development: Install Visual Studio with Azure Development and ASP.NET workloads - Configure git for long paths: `git config --global core.longpaths true` @@ -89,7 +89,7 @@ After making changes, validate by testing these workflows: - `.github/` - GitHub workflows and templates ## Key Technologies -- .NET 8 (see global.json for exact version) +- .NET 10 (see global.json for exact version) - Azure DevOps APIs - ASP.NET Core for web APIs - Entity Framework Core for data access diff --git a/.github/copilot-setup-steps.yml b/.github/copilot-setup-steps.yml deleted file mode 100644 index 8e70e2979f..0000000000 --- a/.github/copilot-setup-steps.yml +++ /dev/null @@ -1,15 +0,0 @@ -name: Copilot Setup Steps - -# These steps will be executed before GitHub Copilot assists with code -# This helps ensure Copilot has access to properly restored dependencies - -steps: -- name: Checkout repository - uses: actions/checkout@v3 - with: - fetch-depth: 0 - -- name: Restore dependencies - run: | - ./eng/common/build.sh -restore - shell: bash diff --git a/.github/hooks/security.json b/.github/hooks/security.json new file mode 100644 index 0000000000..9f075b750b --- /dev/null +++ b/.github/hooks/security.json @@ -0,0 +1,10 @@ +{ + "version": 1, + "hooks": { + "preToolUse": [{ + "type": "command", + "bash": "./scripts/security-check.sh", + "powershell": "pwsh -File scripts/security-check.ps1" + }] + } +} diff --git a/.github/instructions/ef-migrations.instructions.md b/.github/instructions/ef-migrations.instructions.md new file mode 100644 index 0000000000..8dc9410411 --- /dev/null +++ b/.github/instructions/ef-migrations.instructions.md @@ -0,0 +1,8 @@ +--- +applyTo: 'src/Maestro/Maestro.Data/Migrations/**' +--- +# EF Core Migrations +**When to read:** Changing the data model or migrations. + +- These files are generated by `dotnet ef migrations add` — do not hand-edit the generated `*.Designer.cs` or snapshot files. +- Create a new migration for model changes rather than editing existing ones. See `docs/DevGuide.md` for the exact EF commands. diff --git a/.github/instructions/testing.instructions.md b/.github/instructions/testing.instructions.md new file mode 100644 index 0000000000..cbdd3aab7b --- /dev/null +++ b/.github/instructions/testing.instructions.md @@ -0,0 +1,14 @@ +--- +applyTo: 'test/**/*.cs' +--- +# Testing Conventions +**When to read:** Writing or modifying test files. + +- Use NUnit (`[Test]`, `[TestFixture]`), Moq for mocks, AwesomeAssertions for fluent assertions. +- Follow the Arrange-Act-Assert (AAA) pattern. +- Do NOT add tests to `test/ProductConstructionService.ScenarioTests` unless intentionally writing deployed-service scenarios — they require a full service deployment and are excluded from local/agent verification. + +## Codeflow tests +- `test/Darc/Microsoft.DotNet.DarcLib.Codeflow.Tests` are local end-to-end tests: they create real on-disk git repositories and exercise the real codeflow classes through a real `ServiceProvider` (no mocks except the BAR/API client). They require `git` on PATH. +- Extend `CodeFlowTestsBase` for these; reuse its repo/VMR setup and `GitOperations` helper rather than rolling your own. +- They are slower than ordinary unit tests — keep them deterministic and clean up their temp directories. diff --git a/.github/prompts/generate-tests.prompt.md b/.github/prompts/generate-tests.prompt.md new file mode 100644 index 0000000000..f0de1986e6 --- /dev/null +++ b/.github/prompts/generate-tests.prompt.md @@ -0,0 +1,15 @@ +--- +description: Generate NUnit unit tests for the active file following repo conventions. +--- + +# Generate Tests + +Generate unit tests for `${file}` using this repo's conventions. + +- Framework: NUnit (`[Test]`, `[TestFixture]`). +- Mocking: Moq. Assertions: AwesomeAssertions (fluent). +- Structure each test with the Arrange-Act-Assert (AAA) pattern. +- Place the test in the matching `test/` project; reuse existing fixtures/helpers where available. +- Do NOT target `test/ProductConstructionService.ScenarioTests` (requires a deployed service). + + diff --git a/.github/skills/add-darc-command/SKILL.md b/.github/skills/add-darc-command/SKILL.md new file mode 100644 index 0000000000..a4a15b250b --- /dev/null +++ b/.github/skills/add-darc-command/SKILL.md @@ -0,0 +1,43 @@ +--- +name: add-darc-command +description: 'Add a new command (verb) to the DARC CLI. Use when asked to "add a darc command", "create a new darc verb", "add a darc operation", or extend the darc tool with a new subcommand.' +--- + +# Add a DARC CLI Command + +Adds a new verb to the DARC CLI by creating a paired CommandLineOptions class and an Operation class. The `CommandLine` library auto-discovers verbs via the `[Verb]` attribute. + +## When to Use + +- Adding a new `darc ` subcommand. +- Exposing a new BAR / configuration operation through the CLI. + +## Process + +### Step 1: Create the options class +Create `src/Microsoft.DotNet.Darc/Darc/Options/CommandLineOptions.cs`. +- Decorate with `[Verb("my-verb", HelpText = "...")]`. +- Inherit the appropriate base (e.g. `CommandLineOptions`, `ConfigurationManagementCommandLineOptions`) — pick the base used by sibling commands with the same auth/context needs. +- Add `[Option(...)]` properties for each argument; mark required ones `Required = true`. + + +### Step 2: Create the operation class +Create `src/Microsoft.DotNet.Darc/Darc/Operations/Operation.cs`. +- Inherit the matching operation base used by the options' generic parameter. +- Inject services via the constructor (e.g. `IBarApiClient`, `ILogger`). +- Implement `protected override async Task ExecuteInternalAsync()` and return a process exit code. + + +### Step 3: Wire up dependencies + + +## Constraints +- Async methods must end in `Async`; never block with `.Result`/`.Wait()`. +- Use `ILogger` and structural logging; each method logs its own actions. +- Never throw generic `Exception`. +- Match the existing options/operation base-class conventions — do not invent a new pattern. + +## Validation +- `dotnet build` succeeds (warnings are errors in this repo). +- `dotnet run --project src/Microsoft.DotNet.Darc/Darc -- my-verb --help` shows the new command and its options. +- Add/extend unit tests under `test/` (NUnit + Moq + AwesomeAssertions, AAA pattern). diff --git a/.gitignore b/.gitignore index 3b7e679024..d559fd4b55 100644 --- a/.gitignore +++ b/.gitignore @@ -236,3 +236,4 @@ node_modules/ /eng/git-commit-diagram.mmd + diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000..1e8ae16f89 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,35 @@ +# AGENTS.md + +## Verification +- Build & test: `dotnet build` then `dotnet test --no-build` +- The required .NET SDK is pinned in `global.json` — assume it is installed. +- If verification fails, fix the root cause and re-run. + +## Environment +- .NET 10 (see `global.json`). `Directory.Build.props` sets `TreatWarningsAsErrors=true` — unused usings and warnings break the build. +- Build via `dotnet build`, `Build.cmd` (Windows), or `./build.sh` (Linux/macOS); the repo uses the Arcade SDK. + +## Guardrails +- Async methods must have an `Async` suffix; prefer async/await over `.Result`/`.Wait()`. +- Never throw generic `Exception`; use structural logging and `ILogger` (not `ILogger`). +- Prefer immutable types (records / readonly); annotate nullable reference types. +- Tests: NUnit + Moq + AwesomeAssertions, AAA pattern. +- Do NOT run `test/ProductConstructionService.ScenarioTests` — they require a deployed service. + +## Constraints +- Keep diffs minimal and scoped to the request. +- Update or add tests for any behavior change. +- Do not modify CI, dependency versions, or security settings unless asked. +- Never print, log, or commit secrets. + +## Learning from corrections +- When the user corrects you, rejects an approach, or states a durable preference or convention, store it with Copilot Memory (the `store_memory` tool) so it persists across sessions. +- Only store durable, generally-applicable facts — not ephemeral, task-specific instructions ("for this PR…", "just this once…"). +- Before storing, check the surfaced memories; if a similar fact exists, upvote/refine it instead of adding a near-duplicate. +- Keep each memory atomic and short, and don't store anything already covered by AGENTS.md, `.github/instructions/`, or inferable from code — prefer in-repo docs for team conventions, reserving memory for cross-session preferences. +- If a correction contradicts an existing memory, update it: downvote the outdated memory and store the corrected fact. + +## Where to find more +- Detailed conventions & build guide: `.github/copilot-instructions.md` and `docs/DevGuide.md` +- Path-specific rules: `.github/instructions/` +- Multi-step workflows: `.github/skills/*/SKILL.md` diff --git a/scripts/security-check.ps1 b/scripts/security-check.ps1 new file mode 100644 index 0000000000..f0e1d96242 --- /dev/null +++ b/scripts/security-check.ps1 @@ -0,0 +1,10 @@ +$ErrorActionPreference = 'Stop' +# Block destructive commands — customize this blocklist for your repo +$blockedPatterns = @('rm -rf /', 'DROP DATABASE', 'format C:', 'mkfs', 'git push --force') +$commandText = $args -join ' ' +foreach ($pattern in $blockedPatterns) { + if ($commandText -match [regex]::Escape($pattern)) { + Write-Error "Blocked: destructive pattern detected ($pattern)" + exit 1 + } +} diff --git a/scripts/security-check.sh b/scripts/security-check.sh new file mode 100644 index 0000000000..0f04603864 --- /dev/null +++ b/scripts/security-check.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -euo pipefail +# Block destructive commands — customize this blocklist for your repo +BLOCKED_PATTERNS=("rm -rf /" "DROP DATABASE" "format C:" "mkfs" "git push --force") +for pattern in "${BLOCKED_PATTERNS[@]}"; do + if echo "$*" | grep -qi "$pattern"; then + echo "❌ Blocked: destructive pattern detected ($pattern)" >&2 + exit 1 + fi +done diff --git a/scripts/verify b/scripts/verify new file mode 100644 index 0000000000..d0a61d3fd4 --- /dev/null +++ b/scripts/verify @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +set -euo pipefail +# Verify: repeatable health check. Assumes the required .NET SDK (see global.json) is installed. +dotnet build +dotnet test --no-build diff --git a/scripts/verify.ps1 b/scripts/verify.ps1 new file mode 100644 index 0000000000..9b4cf102e6 --- /dev/null +++ b/scripts/verify.ps1 @@ -0,0 +1,6 @@ +$ErrorActionPreference = 'Stop' +# Verify: repeatable health check. Assumes the required .NET SDK (see global.json) is installed. +dotnet build +if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } +dotnet test --no-build +if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } diff --git a/src/AGENTS.md b/src/AGENTS.md new file mode 100644 index 0000000000..6a3e260ce9 --- /dev/null +++ b/src/AGENTS.md @@ -0,0 +1,27 @@ +# AGENTS.md — `src/` project map + +Scope: `src/`. Extends the root AGENTS.md. A brief orientation of what lives where; much dependency-flow work spans several of these projects. + +## DARC CLI — `Microsoft.DotNet.Darc/` +- `Darc` — the DARC command-line tool (verbs = Operations + CommandLineOptions pairs). +- `DarcLib` — the core dependency-flow library (VMR / codeflow, git operations, version files) shared by DARC and PCS. + +## Maestro libraries — `Maestro/` +(Former Maestro service; now shared libraries consumed by PCS.) +- `Maestro.Data` — EF Core data layer and the Build Asset Registry (BAR) `DbContext` + migrations. +- `Maestro.DataProviders` — BAR data-access implementations (SQL BAR client, remote/token factories). +- `Maestro.Common` — shared utilities (git URL helpers, caching, logging, version constants). +- `Maestro.Services.Common` — shared host/service wiring (database, Key Vault, data protection, service defaults). +- `Maestro.WorkItems` — background work-item and reminder infrastructure. +- `Maestro.MergePolicies` / `Maestro.MergePolicyEvaluation` — merge policy definitions and their evaluation logic/models. +- `Microsoft.DotNet.Maestro.Tasks` — MSBuild tasks used by builds to publish build/asset metadata to the BAR. + +## Product Construction Service — `ProductConstructionService/` +See `ProductConstructionService/AGENTS.md` for run/convention details. +- `ProductConstructionService.Api` — main service host (ASP.NET Core API + background workers, Dockerized). +- `ProductConstructionService.AppHost` — .NET Aspire orchestration for running the service locally. +- `ProductConstructionService.DependencyFlow` — core dependency-flow logic: subscription triggering, PR updaters/targets, merge policy evaluation. +- `ProductConstructionService.SubscriptionTriggerer` — job that triggers subscriptions on a schedule. +- `ProductConstructionService.FeedCleaner` — job that cleans up stale package feeds. +- `ProductConstructionService.BarViz` — Blazor web UI for visualizing the Build Asset Registry. +- `Microsoft.DotNet.ProductConstructionService.Client` — generated client for the PCS API; regenerate when API contracts change. diff --git a/src/ProductConstructionService/AGENTS.md b/src/ProductConstructionService/AGENTS.md new file mode 100644 index 0000000000..0e4be9b6a2 --- /dev/null +++ b/src/ProductConstructionService/AGENTS.md @@ -0,0 +1,14 @@ +# AGENTS.md — Product Construction Service (PCS) + +Scope: `src/ProductConstructionService/`. Extends the root AGENTS.md. See `src/AGENTS.md` for the project map. + +## Running locally +- Run the service via the Aspire AppHost: `dotnet run --project ProductConstructionService.AppHost`. +- Requires Docker Desktop running. Do not assume a plain `dotnet run` on the Api project is sufficient. + +## Conventions +- The API uses Newtonsoft.Json with camelCase properties and camelCase string enums; dates are ISO-8601 UTC (`yyyy-MM-ddTHH:mm:ssZ`). Match this when adding contracts. +- API controllers live in `ProductConstructionService.Api/Api/`. + +## Safety +- `test/ProductConstructionService.ScenarioTests` require a fully deployed service — never run them locally or in agent verification.