Skip to content

Baseline Architecture

How the four Progress.* NuGet packages fit together, where they come from, and what each client repo actually owns.


1. The Four Baseline Packages

Every client site references the same four NuGet packages — pinned at 1.* (stable per-major float). Fixes published to the baseline flow into all clients on the next dotnet restore.

Package Contents Consumed where
Progress.Baseline.Core Shared C# services (ISiteSettingsService, IDictionaryService, IArticleService, IGlobalNotificationsService, …), interfaces, view models, DI registration (AddProgressBaselineCore()), configuration defaults (AddProgressBaselineDefaults()), URL rewriting, security middleware. Program.cs — DI registration
Progress.Baseline.Web 190+ Razor views (page templates, BlockGrid/BlockList components, site layout partials), 172 historical client CSS themes, JavaScript, vendor libraries (Bootstrap 5.3.3, jQuery, Slick), uSync baseline schema files (555+ content types, 666 data types, 142 dictionary items, 36 templates). Copies views and assets into the client repo on dotnet build. Client Views/ + wwwroot/ via build-time copy
Progress.CustomPropertyEditors 31 TypeScript-based Umbraco backoffice property editors (headings, testimonials, calculators, FAQ, maps, social, etc.) — packaged as static App_Plugins/ assets. Auto-registered by Umbraco at startup
Progress.LoanCalculator Standalone loan calculation engine (ILoanCalculatorEstimator) plus calculator API and Razor views. Program.cs — DI registration

2. Source of Truth

The baseline is built from a single Azure DevOps repo:

  • Org / Project: https://dev.azure.com/ProgressSystemsLtd / CUCloud.Web
  • Repo: Umbraco.Core (branch: main)
  • Build pipeline: The baseline NuGet build pipeline (pipeline 51 in Azure DevOps)
  • NuGet feed: Azure Artifacts feed CUCloud-Web-Packages https://pkgs.dev.azure.com/ProgressSystemsLtd/CUCloud.Web/_packaging/CUCloud-Web-Packages/nuget/v3/index.json

Authentication for client dotnet restore:

  • Interactive (developer): Install the Azure Artifacts credential provider, then az login
    iex "& { $(irm https://aka.ms/install-artifacts-credprovider.ps1) }"
    dotnet restore --interactive
    
  • PAT: Generate an Azure DevOps PAT with Packaging: Read and embed it in nuget.config
  • CI pipelines: Use the NuGetAuthenticate@1 Azure Pipelines task

3. Release Model

Each commit landed on Umbraco.Core/main triggers Pipeline 51, which:

  1. Builds and tests the solution
  2. Packs the four Progress.* packages
  3. Auto-bumps the patch tag (e.g. 1.0.71.0.8) and pushes the tag
  4. The tag drives MinVer to produce a stable NuGet version (no -preview suffix)
  5. Pushes the stable package to CUCloud-Web-Packages

Major and minor version bumps are manual — performed when intentionally breaking API or shipping a substantial feature set.

Client impact: With Version="1.*" pinned in Progress.Web.csproj, each client picks up the latest stable patch on every dotnet restore. Major bumps require an explicit version change in the client csproj.

How baseline file updates reach the client repodotnet restore pulls the new package, but baseline templates and assets are not silently overwritten in the client's Views/ and wwwroot/ on the next build. See Section 6 — Pulling baseline file updates for the opt-in flow.


4. AddProgressBaselineDefaults()

The template's Program.cs already wires up:

builder.Configuration.AddProgressBaselineDefaults();

This loads a baseline appsettings.json shipped inside Progress.Baseline.Web at the lowest precedence in the configuration chain. It currently covers:

  • Umbraco:CMS:Runtime:Mode (production-appropriate default)
  • Umbraco:CMS:Content:DisallowedUploadedFileExtensions
  • BlockPreview configuration
  • Other baseline-wide settings that should be uniform across all clients

Precedence (lowest → highest):

  1. AddProgressBaselineDefaults() ← baseline defaults
  2. appsettings.json ← client base config
  3. appsettings.{Environment}.json ← per-environment overrides
  4. Environment variables ← container / hosting overrides

To override a baseline default, simply set the same key in client appsettings.json (or a more specific layer). Baseline-wide settings don't need to live in every client repo.


5. What Each Client Repo Owns vs Inherits

Concern Owner Location
Connection string Client appsettings.{Environment}.json
Brand CSS theme Client src/Progress.Web/wwwroot/cssCreditUnion/theme.css
Client logo / brand images Client src/Progress.Web/wwwroot/images/
Client-specific config (Google Maps key, notifications filters, etc.) Client src/Progress.Web/appsettings.json
View overrides (when needed) Client src/Progress.Web/Views/Overrides/ (or edit baseline view in-place after build copy)
Page templates, layouts, BlockGrid components Baseline Progress.Baseline.Web
Backoffice property editors Baseline Progress.CustomPropertyEditors
C# services + DI Baseline Progress.Baseline.Core
Loan calculator engine Baseline Progress.LoanCalculator
Umbraco schema (content types, data types, templates) Baseline uSync files in Progress.Baseline.Web, imported on first run

Rule of thumb: if the change is brand/data/config, it lives in the client repo. If it's structural or shared, it lives in Umbraco.Core and ships via the next baseline release.


6. Pulling Baseline File Updates

Progress.Baseline.Web ships its views and static assets as physical files that land in the client Views/ and wwwroot/ folders at build time. Umbraco requires this — the backoffice template editor and Razor view engine read from disk, not from a NuGet's bin/ output.

Because those files are physically present and tracked by git, the build target follows an opt-in update model so a dotnet restore of a newer package version never silently overwrites client customisations.

Default behaviour (no flag)

On every dotnet build:

  • New files present in the package but not yet on disk → copied in. This is how new clients get the full baseline on first restore and how brand-new components in a baseline release reach existing clients.
  • Existing files → never touched. Local edits — whether changed in the IDE or saved via the Umbraco backoffice — survive every build.
  • A one-line informational hint is logged once existing files are detected:
Baseline views: to pull package updates into existing files,
run 'dotnet build /p:BaselineUpdate=true' and review with 'git diff'.

Pulling updates from a newer baseline package

When a baseline release fixes or improves a view/asset you have not customised, opt in explicitly:

dotnet build /p:BaselineUpdate=true

This copies every baseline file from the package over the local copy. Copy SkipUnchangedFiles="true" means identical files are a no-op — only genuinely different files end up in git diff. The dev then:

  1. git diff -- Views/ wwwroot/ — review every change.
  2. Keep, revert, or merge each modified file (file-by-file).
  3. Commit what you want to keep.

CI / CD safety guarantee

CI pipelines never pass /p:BaselineUpdate=true. Whatever is committed to the client repo is what ships — automated builds cannot silently clobber committed overrides. Override behaviour is a deliberate developer action.

Local vs RCL view resolution

Razor view resolution picks the local file in Views/ ahead of any embedded view from a Razor Class Library. After dotnet build the baseline view is always physically present on disk, so the file you see in Views/ is the file Umbraco renders and the file the backoffice template editor opens.

Resetting a single file to baseline

Delete it and rebuild — the default "copy new files" path re-creates it:

rm Views/Partials/SiteLayout/footer.cshtml
dotnet build

Historical: Views/Views nesting (baseline ≤ 1.0.0)

Baseline 1.0.0 had a PackagePath bug in Progress.Baseline.Web.csproj that materialised templates into Views/Views/… instead of Views/…. Symptom: the Umbraco backoffice template editor opened blank for affected templates while the file existed on disk. Fixed in Progress.Baseline.Web 1.0.1. Clients with a stale install must clean up before rebuilding with a newer baseline:

rm -rf src/Progress.Web/Views/Views/          # the nested misplaced copy
dotnet restore
dotnet build

After the rebuild, the 1.0.1+ target copies every view to the correct flat Views/<path>/<file>.cshtml location and the backoffice editor renders the content as expected.


Migration documentation by Double for Progress Credit Union