This guide explains how to contribute to the Umbraco.AI monorepo, covering branch naming conventions, git workflows, and release processes.
- Getting Started
- Branch Naming Convention
- Development Workflow
- Pull Request Process
- Release Process
- CI/CD Pipeline
- Coding Standards
- .NET 10.0 SDK
- Node.js 20.x
- Git
- SQL Server or SQLite (for database development)
- IDE: Visual Studio 2022, VS Code, or JetBrains Rider
# Clone the repository
git clone https://github.com/umbraco/Umbraco.AI.git
cd Umbraco.AI
# Run setup script (creates unified solution + demo site)
.\scripts\install-demo-site.ps1 # Windows
./scripts/install-demo-site.sh # Linux/Mac
# Configure git hooks (enforces branch naming and commit message syntax)
.\scripts\setup-git-hooks.ps1 # Windows
./scripts/setup-git-hooks.sh # Linux/Mac
# Open unified solution
start Umbraco.AI.local.slnxUmbraco.AI/ # Monorepo root
├── Umbraco.AI/ # Core AI layer (1.x)
├── Umbraco.AI.Agent/ # Agent add-on (1.x)
├── Umbraco.AI.Agent.UI/ # Agent UI library (1.x)
├── Umbraco.AI.Agent.Copilot/ # Agent copilot UI (1.x)
├── Umbraco.AI.Prompt/ # Prompt add-on (1.x)
├── Umbraco.AI.OpenAI/ # OpenAI provider (1.x)
├── Umbraco.AI.Anthropic/ # Anthropic provider (1.x)
├── Umbraco.AI.Amazon/ # Amazon Bedrock provider (1.x)
├── Umbraco.AI.Google/ # Google Gemini provider (1.x)
├── Umbraco.AI.MicrosoftFoundry/ # Microsoft AI Foundry provider (1.x)
├── demo/ # Demo site (generated)
└── docs/ # Shared documentation
All branches MUST follow these patterns. This is enforced by git hooks and CI/CD.
| Pattern | Description | Example |
|---|---|---|
main |
Main development branch | main |
dev |
Integration branch | dev |
support/<anything> |
Long-term support branches | support/1.x, support/2.x |
feature/<anything> |
New feature development | feature/add-embeddings |
release/<anything> |
Release preparation | release/2026.01 |
hotfix/<anything> |
Emergency fixes | hotfix/2026.01.1 |
While the pattern allows <anything> after the prefix, we recommend these conventions for consistency:
Release branches: release/YYYY.MM
YYYY.MM= Year and month of the release- Example:
release/2026.01for a January 2026 release - Example:
release/2026.12for a December 2026 release
Hotfix branches: hotfix/YYYY.MM.N
YYYY.MM= Year and month.N= Sequential number (1st, 2nd, 3rd hotfix in that period)- Example:
hotfix/2026.01.1for the first hotfix in January 2026 - Example:
hotfix/2026.01.2for the second hotfix in January 2026
Benefits of this convention:
- Calendar-based organization makes it easy to find branches chronologically
- Clear distinction between regular releases (monthly cadence) and hotfixes (emergency patches)
- Sequential hotfix numbering prevents branch name conflicts
- Independent from product version numbers (which follow semantic versioning)
Note: This is a recommendation, not a requirement. The validation only enforces the prefix pattern (release/ or hotfix/).
Correct:
feature/add-streaming-support
feature/improve-context-handling
feature/add-versioning
release/2026.01 # Recommended: calendar-based
release/v1.1.0 # Valid: version-based
hotfix/2026.01.1 # Recommended: calendar-based with sequence
hotfix/critical-security-fix # Valid: descriptive nameIncorrect:
feature-add-streaming # Wrong delimiter
release-2026.01 # Wrong delimiterBranch naming is enforced at two levels:
- Git Hooks (
.githooks/pre-push): Local validation before push - Azure DevOps CI/CD: Validation in pipeline (cannot be bypassed)
The repository includes several git hooks to manage release-manifest.json lifecycle:
Protection on release/hotfix branches:
- pre-merge-commit hook: Automatically restores
release-manifest.jsonif it gets deleted during a merge to arelease/*orhotfix/*branch (e.g., when merging fromdev) - merge driver: Preserves
release-manifest.jsonwhen there are content conflicts (defense-in-depth)
Cleanup on long-term branches:
- post-merge hook: Automatically removes
release-manifest.jsonafter merging tomain,dev, orsupport/*branches (these branches should never have the manifest file)
This ensures:
- ✅ Release branches always keep their manifest during merges
- ✅ Long-term branches never accumulate manifest files
- ✅ No manual intervention needed
To bypass git hooks temporarily (not recommended):
git push --no-verify# 1. Create feature branch from main
git checkout main
git pull origin main
git checkout -b feature/add-embeddings
# 2. Make changes in the product directory
# Edit: Umbraco.AI/src/Umbraco.AI.Core/...
# 3. Build and test
dotnet build Umbraco.AI/Umbraco.AI.slnx
dotnet test Umbraco.AI/Umbraco.AI.slnx
# 4. Test in demo site
cd demo/Umbraco.AI.DemoSite
dotnet run
# 5. Commit changes
git add .
git commit -m "feat(core): add embedding support
Implements IChatClient.EmbeddAsync using M.E.AI abstractions"
# 6. Push and create PR
git push -u origin feature/add-embeddingsWhen a feature spans multiple products (e.g., Core + Agent):
# 1. Create feature branch
git checkout -b feature/shared-context
# 2. Make changes to both products
# Edit: Umbraco.AI/src/Umbraco.AI.Core/...
# Edit: Umbraco.AI.Agent/src/Umbraco.AI.Agent.Core/...
# 3. Build unified solution (tests everything together)
dotnet build Umbraco.AI.local.slnx
# 4. Test in demo site
cd demo/Umbraco.AI.DemoSite
dotnet run
# 5. Commit atomic changes
git add .
git commit -m "feat(core,agent): add shared context handling
- Core: Add IContextProvider interface
- Agent: Implement context sharing between agents"
# 6. Push and create PR
git push -u origin feature/shared-contextNote: Use a descriptive branch name that reflects the scope of the work.
# Watch all frontends in parallel (hot reload)
npm run watch
# Or watch specific product
npm run watch:core
npm run watch:agent
# Generate OpenAPI clients (demo site must be running)
npm run generate-clientBy default, all products use project references to Core (changes visible immediately):
<!-- Agent.Core.csproj -->
<ProjectReference Include="..\..\..\Umbraco.AI\src\Umbraco.AI.Core\Umbraco.AI.Core.csproj"
Condition="'$(UseProjectReferences)' == 'true'" />This means:
- Local builds: Agent/Prompt/Providers automatically use your local Core changes
- Distribution builds: CI/CD builds with
UseProjectReferences=falsefor package references
# Run tests for specific product
dotnet test Umbraco.AI/Umbraco.AI.slnx
dotnet test Umbraco.AI.Agent/Umbraco.AI.Agent.slnx
# Run all tests
dotnet test Umbraco.AI.local.slnxUse conventional commits format:
<type>(<scope>): <description>
Types: feat, fix, docs, chore, refactor, test, perf
Scopes: core, agent, prompt, openai, anthropic
Examples:
feat(core): add streaming chat support
fix(agent): resolve context memory leak
docs(prompt): update README with examples
chore(core,agent): update dependencies
## Summary
Brief description of what this PR does.
## Changes
- List of key changes
- Another change
## Testing
- [ ] Unit tests pass
- [ ] Integration tests pass
- [ ] Tested in demo site
- [ ] Frontend builds successfully (if applicable)
## Breaking Changes
None / List any breaking changes
## Related Issues
Closes #123Before submitting a PR:
- Branch name follows convention (
feature/<anything>) - Code follows coding standards
- All tests pass
- Frontend builds (if frontend changes)
- Documentation updated (if needed)
- CLAUDE.md updated (if architecture changes)
- No console errors or warnings
- Automated Checks: GitHub Actions runs branch validation + unit tests
- Code Review: At least one team member must approve
- CI/CD: Azure DevOps builds affected products
- Merge: Squash merge to main (keeps history clean)
Each product is versioned and released independently using Nerdbank.GitVersioning (NBGV).
| Product | Version Scheme | Current |
|---|---|---|
| Umbraco.AI (Core) | 1.x (independent) | 1.0.0 |
| Umbraco.AI.Agent | 1.x (independent) | 1.0.0 |
| Umbraco.AI.Prompt | 1.x (independent) | 1.0.0 |
| Umbraco.AI.OpenAI | 1.x (independent) | 1.0.0 |
| Umbraco.AI.Anthropic | 1.x (independent) | 1.0.0 |
| Umbraco.AI.Amazon | 1.x (independent) | 1.0.0 |
| Umbraco.AI.Google | 1.x (independent) | 1.0.0 |
| Umbraco.AI.MicrosoftFoundry | 1.x (independent) | 1.0.0 |
git checkout main
git pull origin main
git checkout -b release/2026.01Use the interactive script to generate release-manifest.json:
# Windows
.\scripts\generate-release-manifest.ps1
# Linux/Mac
./scripts/generate-release-manifest.shThe script will scan for all products and present an interactive multiselect interface. Alternatively, create the file manually:
["Umbraco.AI", "Umbraco.AI.OpenAI"]Edit each product's version.json in the manifest:
{
"version": "1.1.0",
"assemblyVersion": {
"precision": "build"
},
"publicReleaseRefSpec": ["^refs/heads/main$", "^refs/heads/release/", "^refs/heads/hotfix/"]
}git add release-manifest.json Umbraco.AI/version.json
git commit -m "chore(release): prepare 2026.01"
git push -u origin release/2026.01Azure DevOps detects the release/* branch pattern:
- Enforces
release-manifest.json(CI fails if any changed product is missing from the list) - Builds and packs only the listed products
- Publishes two artifacts:
all-nuget-packages- Contains all NuGet packages (.nupkg)all-npm-packages- Contains all npm packages (.tgz)
- Publishes
pack-manifestartifact - Contains metadata for each package (name, version, type)
The Azure DevOps release pipeline automatically triggers after the build completes:
-
Download Artifacts
- Downloads
all-nuget-packagesartifact (contains all .nupkg files) - Downloads
all-npm-packagesartifact (contains all .tgz files) - Downloads
pack-manifestartifact (contains package metadata)
- Downloads
-
Deploy Packages
- Deploys NuGet packages to MyGet (pre-release feed)
- Deploys npm packages to npm registry with
@nexttag
-
Tag Git Repository
- Reads
pack-manifestartifact to get each package name and version - Creates git tag for each deployed package:
[Product_Name]@[Version] - Examples:
[email protected],[email protected] - Tags are pushed to the repository
- Reads
MyGet URL: https://www.myget.org/F/umbraco-ai/api/v3/index.json
Before merging to main, validate the pre-release packages work correctly:
Option A: Automated Test Site (Recommended)
Use the package test site script to create a fresh Umbraco site with all packages:
# Windows
.\scripts\install-package-test-site.ps1 -Feed prereleases -Force
# Linux/Mac
./scripts/install-package-test-site.sh --feed=prereleases --forceThis creates a site at demo/Umbraco.AI.PackageTestSite with:
- All Umbraco.AI packages from MyGet prereleases feed
- Clean starter kit
- Configured package source mapping
Option B: Manual Testing
# Add MyGet feed
dotnet nuget add source https://www.myget.org/F/umbraco-ai/api/v3/index.json -n UmbracoAI
# Install pre-release package
dotnet add package Umbraco.AI.Core --version 1.1.0-*
# Install pre-release npm package
npm install @umbraco-ai/core@nextVerify the site:
cd demo/Umbraco.AI.PackageTestSite # or your test site folder
dotnet run
# Open https://localhost:44355 in your browser
# Test all features affected by the releaseIf issues are found, fix them on the release branch and repeat the testing.
Once testing passes, trigger the production release from Azure DevOps. The release pipeline:
-
Download Artifacts
- Downloads
all-nuget-packagesartifact from the build - Downloads
all-npm-packagesartifact from the build - Downloads
pack-manifestartifact
- Downloads
-
Deploy to Production
- Deploys NuGet packages to NuGet.org
- Deploys npm packages to npm registry with
@latesttag
-
Tag Git Repository
- Reads
pack-manifestto get each package name and version - Creates git tag for each deployed package:
[Product_Name]@[Version] - Examples:
[email protected],[email protected] - Tags are pushed to the repository
- Reads
NuGet URL: https://www.nuget.org/packages/Umbraco.AI.Core
npm URL: https://www.npmjs.com/package/@umbraco-ai/core
After deployment to NuGet.org, verify the stable packages work correctly:
# Windows
.\scripts\install-package-test-site.ps1 -Feed release -SiteName "Umbraco.AI.ReleaseSite" -Force
# Linux/Mac
./scripts/install-package-test-site.sh --feed=release --name="Umbraco.AI.ReleaseSite" --forceThis creates a site at demo/Umbraco.AI.ReleaseSite with:
- All Umbraco.AI packages from NuGet.org (stable versions only)
- No pre-release packages
- Simplified NuGet.config (no PackageSourceMapping needed)
Verify the site:
cd demo/Umbraco.AI.ReleaseSite
dotnet run
# Open https://localhost:44355 in your browser
# Verify all features work correctly with production packagesUse the /post-release-cleanup skill to automate this entire step:
/post-release-cleanupThis will:
- Detect released products from git tags on the release branch
- Merge the release branch into
main(no-ff) - Merge
mainintodev(no-ff) - Bump
version.jsonondevfor each released product (patch increment, e.g.,1.5.0→1.5.1) - Optionally delete the release/hotfix branch (local + remote)
The version bump on dev ensures nightly builds produce versions higher than the released version.
Manual alternative:
# Merge to main
git checkout main
git pull origin main
git merge release/2026.01 --no-ff
git push origin main
# Merge main to dev and bump versions
git checkout dev
git pull origin dev
git merge main --no-ff
# Bump version.json for each released product (patch increment)
git push origin dev
# Clean up release branch
git branch -d release/2026.01
git push origin --delete release/2026.01Automatic Cleanup: When you merge to main, dev, or support/*, the post-merge git hook automatically removes release-manifest.json and commits the cleanup. You'll see:
🧹 Cleaning up release-manifest.json after merge to main...
✓ release-manifest.json removed and committed
Note on Git Tags: The release pipeline automatically creates git tags during deployment:
- Product-specific tags (e.g.,
[email protected]) track each deployed package version - These tags reference the exact commit that was built and released
- Use these tags as base points for hotfix branches or to trace which code version is in production
For emergency fixes to production:
# 1. Create hotfix branch from the production tag
# Find the specific product version that needs fixing
git tag --list | grep "Umbraco.AI@"
# Example output: [email protected], [email protected]
# Branch from the specific product tag
git checkout -b hotfix/2026.01.1 [email protected]
# If multiple products need hotfixes, branch from main or support branch
# git checkout -b hotfix/2026.01.1 main
# git checkout -b hotfix/2026.01.1 support/1.x
# 2. Fix the issue
# Edit: Umbraco.AI/src/...
# 3. Update version.json for affected products
# Change: "version": "1.1.1"
# Edit: Umbraco.AI/version.json
# 4. Generate changelog for the hotfix
npm run changelog -- --product=Umbraco.AI --version=1.1.1
# Review and edit the changelog entry
# 5. (Optional) Add release-manifest.json if you want an explicit pack list
# On hotfix/* branches, the manifest is optional:
# - If present: Only listed products are packed (enforced)
# - If absent: Change detection is used (automatic)
# Use interactive script (recommended):
.\scripts\generate-release-manifest.ps1 # Windows
./scripts/generate-release-manifest.sh # Linux/Mac
# Or create manually:
echo '["Umbraco.AI"]' > release-manifest.json
# 6. Commit and push
git add Umbraco.AI/CHANGELOG.md release-manifest.json Umbraco.AI/version.json
git commit -m "fix(core): resolve critical security issue"
git push -u origin hotfix/2026.01.1
# 7. Build pipeline runs
# - Changelog validation runs (same as release branches)
# - Packs affected products (per manifest or change detection)
# - Publishes artifacts: all-nuget-packages, all-npm-packages, pack-manifest
# 8. Release pipeline deploys to MyGet and creates pre-release tags
# Tags example: [email protected]
# 9. Test hotfix packages
dotnet add package Umbraco.AI.Core --version 1.1.1-*
# 10. Trigger production release from Azure DevOps
# - Release pipeline deploys to NuGet.org and npm registry
# - Automatically creates production tags: [email protected]
# 11. Merge hotfix to main (or support branch)
# Create PR: hotfix/2026.01.1 → main (or support/1.x)
# After approval and merge, post-merge hook automatically removes release-manifest.json
# Then delete hotfix branchNote: If the hotfix targets a support branch (e.g., support/1.x), the post-merge hook will also clean up release-manifest.json automatically.
To release multiple products in a single release:
- Create
release-manifest.jsonusing the interactive script:
# Windows
.\scripts\generate-release-manifest.ps1
# Linux/Mac
./scripts/generate-release-manifest.shOr manually create the file at repo root:
["Umbraco.AI", "Umbraco.AI.OpenAI", "Umbraco.AI.Anthropic"]-
Update
version.jsonfor each listed product -
Push release branch - CI enforces that all listed products are packed
-
Release pipeline creates tags for each product:
Important: On release/* branches, release-manifest.json is required. CI will fail if any changed product is missing from the list. This ensures intentional releases and prevents accidental package publishing.
On hotfix/* branches, the manifest is optional. If present, it is enforced the same way; if absent, change detection is used automatically.
Add-on packages and providers depend on Umbraco.AI (Core). When releasing products with dependencies, follow these guidelines:
Always use version ranges for cross-product dependencies. This allows add-ons to work with a range of Core versions without requiring simultaneous releases.
Example: If Umbraco.AI.Prompt 1.1.0 is compatible with Core 1.1.x and later within the 1.x series:
<!-- Umbraco.AI.Prompt/Directory.Packages.props -->
<Project>
<ItemGroup>
<!-- Use a range: minimum version 1.1.0, up to (but not including) 1.999.999 -->
<PackageVersion Include="Umbraco.AI.Core" Version="[1.1.0, 1.999.999)" />
</ItemGroup>
</Project>The range format [minimum, maximum) means:
[= inclusive lower bound (>= 1.1.0))= exclusive upper bound (< 1.999.999)- Result: accepts any 1.x version from 1.1.0 onwards
- Root level (
Directory.Packages.propsat repo root): Defines default package versions for all products - Product level (
ProductFolder/Directory.Packages.props): Overrides specific package versions for that product only - During local development: Project references (
UseProjectReferences=true) bypass NuGet versions - During CI/CD build: Distribution builds (
UseProjectReferences=false) use the specified NuGet version ranges
When releasing Core with breaking changes:
- Bump Core minor version:
1.1.0→1.2.0 - Update dependent products: Update their
Directory.Packages.propsto the new minimum version:<PackageVersion Include="Umbraco.AI.Core" Version="[1.2.0, 1.999.999)" />
- Include in release manifest: All dependent products must be included in the same release:
["Umbraco.AI", "Umbraco.AI.Prompt", "Umbraco.AI.Agent"]
| Scenario | Range Format | Example | Description |
|---|---|---|---|
| Minor version series | [X.Y.0, X.999.999) |
[1.1.0, 1.999.999) |
Min 1.1.0, accepts all 1.x |
| Specific minimum | [X.Y.Z, X.999.999) |
[1.1.5, 1.999.999) |
Min 1.1.5, accepts all 1.x |
| Exact version | [X.Y.Z] |
[1.1.0] |
Avoid - prevents any updates |
Best Practice: Use [X.Y.0, X.999.999) format where X.Y.0 is the minimum supported Core version. This allows all future patch and minor releases within the major version.
Before releasing, verify dependencies resolve correctly:
# Build with NuGet references (not project references)
dotnet build Umbraco.AI.local.slnx /p:UseProjectReferences=false
# Verify the correct Core version is resolved
dotnet list Umbraco.AI.Prompt/src/Umbraco.AI.Prompt.Core package --include-transitive | grep Umbraco.AI.CoreEach product maintains its own CHANGELOG.md file at the product root, auto-generated from git history using conventional commits. Changelogs follow the Keep a Changelog format.
All commits should follow the Conventional Commits specification:
<type>(<scope>): <description>
[optional body]
[optional footer]
Type - The kind of change:
feat: New featurefix: Bug fixrefactor: Code refactoringperf: Performance improvementdocs: Documentation onlytest: Tests onlychore: Maintenanceci: CI/CD changesbuild: Build system changesrevert: Reverts a previous commit
Scope - The product or feature area affected (see table below)
Description - Brief summary in present tense (e.g., "add streaming support")
Scopes are automatically discovered from product changelog.config.json files:
| Product | Scopes |
|---|---|
| Umbraco.AI | core, profile, chat, embedding, connection, middleware, registry, settings, providers, ui, frontend, api |
| Umbraco.AI.Agent | agent |
| Umbraco.AI.Agent.UI | agent-ui |
| Umbraco.AI.Agent.Copilot | copilot, tools, approval |
| Umbraco.AI.Prompt | prompt |
| Umbraco.AI.OpenAI | openai |
| Umbraco.AI.Anthropic | anthropic |
| Umbraco.AI.Amazon | amazon |
| Umbraco.AI.Google | google |
| Umbraco.AI.MicrosoftFoundry | microsoft-foundry |
| Meta scopes | deps, ci, docs, release |
Examples:
# Single product
feat(chat): add streaming support
fix(openai): handle rate limit errors correctly
docs(prompt): update template examples
# Multiple products
feat(core,agent): add shared context API
fix(openai,anthropic): standardize error handling
# Breaking changes
feat(core): redesign profile API
BREAKING CHANGE: Profile.GetByName() removed, use GetByAlias() insteadChangelogs are generated manually before creating a release in Azure DevOps.
List available products:
npm run changelog:list
# Or: node scripts/generate-changelog.js --listGenerate changelog for a specific product:
# Using npm script
npm run changelog -- --product=Umbraco.AI --version=1.1.0
# Using Node.js directly
node scripts/generate-changelog.js --product=Umbraco.AI --version=1.1.0
# Using PowerShell wrapper
.\scripts\generate-changelog.ps1 -Product Umbraco.AI -Version 1.1.0
# Using Bash wrapper
./scripts/generate-changelog.sh --product=Umbraco.AI --version=1.1.0Generate unreleased changes:
npm run changelog -- --product=Umbraco.AI --unreleasedGenerate all changelogs at once:
npm run changelog:allWhen creating a release, follow these steps:
-
Create release branch:
git checkout -b release/2026.01
-
Create release manifest using the interactive script:
.\scripts\generate-release-manifest.ps1 # Windows ./scripts/generate-release-manifest.sh # Linux/Mac
Or manually create
release-manifest.json:["Umbraco.AI", "Umbraco.AI.OpenAI"]
-
Update version.json for each product in the manifest
-
Generate changelogs for each product:
npm run changelog -- --product=Umbraco.AI --version=1.1.0 npm run changelog -- --product=Umbraco.AI.OpenAI --version=1.2.0
-
Review and edit generated changelogs (if needed):
- Check that entries are accurate
- Add context to commit messages if needed
- Group related changes
- Highlight important changes
-
Commit changelogs:
git add Umbraco.AI/CHANGELOG.md Umbraco.AI.OpenAI/CHANGELOG.md git commit -m "docs(core,openai): update CHANGELOGs for release 2026.01" -
Commit version updates:
git add release-manifest.json Umbraco.AI/version.json Umbraco.AI.OpenAI/version.json git commit -m "chore(release): prepare 2026.01" -
Push release branch:
git push -u origin release/2026.01
-
Azure DevOps validates and builds:
- Changelog validation runs automatically (release and hotfix branches)
- Verifies CHANGELOG.md exists for each product in manifest
- Checks CHANGELOG.md was updated in recent commits
- Validates version in CHANGELOG.md matches version.json
- Build fails if validation fails - fix issues and push again
- Builds and publishes to MyGet (pre-release)
- Changelog validation runs automatically (release and hotfix branches)
-
Test packages from MyGet
-
Trigger production release from Azure DevOps
- Release pipeline deploys to NuGet.org and npm
- Automatically creates git tags:
[email protected],[email protected] - Tags include the changelog commits
-
Merge release branch to main
To add changelog support for a new product:
-
Create product directory:
Umbraco.AI.NewProduct/ -
Create changelog config:
// Umbraco.AI.NewProduct/changelog.config.json { "scopes": ["new-product"] }
-
Verify discovery:
npm run changelog:list # Should show your new product automatically! -
Generate initial changelog:
npm run changelog -- --product=Umbraco.AI.NewProduct --unreleased
No script changes needed - products are discovered automatically by convention!
The repository uses commitlint to validate commit messages. Invalid commits will show warnings but are still allowed.
Setup validation hooks:
.\scripts\setup-git-hooks.ps1 # Windows
./scripts/setup-git-hooks.sh # Linux/MacThis enables:
- commit-msg hook: Validates commit messages using commitlint (warnings only)
- pre-push hook: Validates branch naming conventions (blocks invalid names)
- post-merge hook: Cleans up
release-manifest.jsonafter merge to main/dev/support/* - pre-merge-commit hook: Restores
release-manifest.jsonon release/hotfix branches if deleted during merge - merge driver: Preserves
release-manifest.jsonon release/hotfix branches (content conflicts only)
If the Azure DevOps build fails with changelog validation errors on a release branch:
Error: "CHANGELOG.md not found"
# Generate the missing changelog
npm run changelog -- --product=<ProductName> --version=<Version>
git add <Product>/CHANGELOG.md
git commit -m "docs(<scope>): add CHANGELOG for v<Version>"
git pushError: "Version mismatch"
# The version in CHANGELOG.md doesn't match version.json
# Either update the changelog version manually, or regenerate it:
npm run changelog -- --product=<ProductName> --version=<Version>
git add <Product>/CHANGELOG.md
git commit -m "docs(<scope>): update CHANGELOG version to v<Version>"
git pushWarning: "CHANGELOG.md does not appear to have been updated"
- This is a warning, not an error - build will still pass
- Indicates the CHANGELOG.md exists but wasn't modified in recent commits
- Usually means you forgot to regenerate the changelog for this release
- Regenerate and commit to resolve
If release-manifest.json gets deleted when merging dev into a release branch:
Scenario: Manifest deleted during merge
This should be automatically prevented by the pre-merge-commit hook, but if it still happens:
# 1. Verify git hooks are configured
git config --get core.hooksPath
# Should output: .githooks
# 2. If not configured, run setup script
.\scripts\setup-git-hooks.ps1 # Windows
./scripts/setup-git-hooks.sh # Linux/Mac
# 3. If manifest was deleted, restore it manually
git show HEAD:release-manifest.json > release-manifest.json
git add release-manifest.json
git commit -m "fix(ci): restore release-manifest.json"
git pushWhy this happens:
- Git's merge drivers are only invoked for content conflicts, not file deletions
- The
pre-merge-commithook detects and restores the file automatically - If hooks aren't configured, the file will be deleted during merge
Testing your commit message:
# Test a commit message
echo "feat(chat): add streaming" | npx commitlint
# Check recent commits
npx commitlint --from HEAD~5 --to HEADThe CI/CD pipeline consists of two main stages:
-
Build Pipeline - Triggered by commits to
release/*,hotfix/*, or other branches- Builds and tests products
- Creates NuGet and npm packages
- Publishes artifacts for deployment
-
Release Pipeline - Triggered by build completion or git tags
- Downloads artifacts from build pipeline
- Deploys packages to package feeds
- Tags git repository with package versions
Each build produces the following artifacts:
| Artifact Name | Contents | Used By |
|---|---|---|
all-nuget-packages |
All .nupkg files from the build | Release pipeline (NuGet deployment) |
all-npm-packages |
All .tgz files from the build | Release pipeline (npm deployment) |
pack-manifest |
JSON metadata for each package (name, version, type) | Release pipeline (git tagging) |
Example pack-manifest content:
[
{
"name": "Umbraco.AI",
"version": "1.1.0",
"type": "nuget"
},
{
"name": "@umbraco-ai/core",
"version": "1.1.0",
"type": "npm"
}
]The release pipeline automatically creates git tags for traceability:
| Tag Format | Example | Purpose | Created When |
|---|---|---|---|
<Product>@<Version> |
[email protected] |
Tracks deployed package version | Automated (by release pipeline) |
<Product>@<Version> |
[email protected] |
Tracks deployed package version | Automated (by release pipeline) |
How it works:
- Release pipeline reads
pack-manifestartifact - For each package in the manifest, creates a git tag:
[Product_Name]@[Version] - Tags are pushed to the repository pointing to the commit that was built and deployed
Benefits:
- Trace which exact commit was deployed for each package
- Navigate to source code for any production version
- Use tags as base points for hotfix branches
- Compare versions across products (e.g.,
git log [email protected]@1.1.0)
The Azure DevOps pipeline uses smart change detection to build only affected products:
Branch Builds:
# Analyze git diff
$changedFiles = git diff --name-only HEAD~1 HEAD
# Determine changed products
if ($file.StartsWith("Umbraco.AI/")) {
$changedProducts["core"] = $true
}
# No dependency propagation (only products with direct changes pack)Release Branches:
release/*branches requirerelease-manifest.jsonand pack only the listed products.hotfix/*branches honor the manifest if present; otherwise, change detection is used.
graph TB
A[DetectChanges] --> B[Build]
B --> C[Test]
B --> D[PublishArtifacts]
D --> E[ReleasePipeline]
E --> F{Deploy Type}
F -->|release/* branch| G[MyGet/npm@next]
F -->|release-* tag| H[NuGet/npm@latest]
G --> I[TagRepository]
H --> I[TagRepository]
1. DetectChanges
- Analyzes git changes or reads
release-manifest.json - Sets variables:
CoreChanged,AgentChanged, etc. - Enforces manifest requirements on
release/*branches
2. Build (Parallel)
- Builds only changed products (or manifest-listed products)
- Uses
UseProjectReferences=falsefor distribution builds - Generates NuGet packages (.nupkg) and npm packages (.tgz)
- Creates
pack-manifestwith metadata
3. Test (Parallel)
- Runs unit tests for changed products
- Runs integration tests where applicable
- Publishes code coverage reports
4. PublishArtifacts
- Publishes
all-nuget-packagesartifact - Publishes
all-npm-packagesartifact - Publishes
pack-manifestartifact
5. ReleasePipeline (triggered on build completion or git tag)
- Downloads artifacts from build pipeline
- Validates package integrity
- Determines deployment target (pre-release vs production)
6. DeployMyGet (on release/* or hotfix/* branches)
- Deploys NuGet packages to MyGet feed
- Deploys npm packages with
@nexttag - URL:
https://www.myget.org/F/umbraco-ai/api/v3/index.json
7. DeployProduction (on release-* or hotfix-* tags)
- Deploys NuGet packages to NuGet.org
- Deploys npm packages with
@latesttag - URLs:
https://www.nuget.org/,https://www.npmjs.com/
8. TagRepository (all deployments)
- Reads
pack-manifestartifact - Creates git tag for each package:
<Product>@<Version> - Pushes tags to repository (e.g.,
[email protected]) - Tags point to the commit that was built
All contributions must follow the coding standards in CLAUDE.md.
Method Naming:
// Async methods: [Action][Entity]Async
Task<AIProfile?> GetProfileAsync(Guid id, CancellationToken ct);
Task<IEnumerable<AIAgent>> GetAllAgentsAsync(CancellationToken ct);Repository Access:
// Only access your own repository
public class AIProfileService
{
private readonly IAIProfileRepository _profileRepository; // ✓ Own repo
private readonly IAIConnectionRepository _connectionRepository; // ✗ Other repo
}Extension Methods:
// Must be in .Extensions namespace
namespace Umbraco.AI.Extensions
{
public static class ChatClientExtensions { }
}Reviewers should check:
- Follows method naming conventions
- No cross-repository access (services use services, not repositories)
- Extension methods in correct namespace
- Async methods have CancellationToken
- No hardcoded strings (use constants or resources)
- Tests included for new functionality
- No breaking changes without discussion
Update documentation when:
- Adding new features or public APIs
- Changing build/deployment process
- Modifying architecture or patterns
- Adding new dependencies
| Type | Location |
|---|---|
| Product-specific guidance | <Product>/CLAUDE.md |
| Shared coding standards | CLAUDE.md |
| Contributing guide | CONTRIBUTING.md (this file) |
| Monorepo structure | docs/migration-guide.md |
| User guides | docs/<topic>.md |
| API documentation | XML comments in code |
- Search existing issues: GitHub Issues
- Ask on Discord: Umbraco Discord Server
- Create new issue: Provide minimal reproduction
Include:
- Product and version (e.g., Umbraco.AI.Core 1.0.0)
- Umbraco CMS version
- .NET version
- Steps to reproduce
- Expected vs actual behavior
- Stack trace (if applicable)
Include:
- Which product(s) would be affected
- Use case / problem to solve
- Proposed API or interface
- Breaking change considerations
By contributing, you agree that your contributions will be licensed under the same license as the Umbraco.AI project.
Thank you for contributing to Umbraco.AI! 🚀