Skip to the content.

Development

Everything you need to run this package locally, change it, and verify the change.

The repository contains the package itself plus a complete Umbraco site to run it in. That site is a development harness — it is never shipped, and IsPackable is false on it.

src/Cms/            the Umbraco host: content, templates, uSync export, dev certificate
src/TrueCopy/  the package
src/TrueCopy.Tests/  its tests
scripts/            tooling that is not part of the build
docs/               these documents

Prerequisites

Nothing else. The site uses SQLite, created on first run.

First run

git clone https://github.com/SteveVaneeckhout/Our.Umbraco.TrueCopy.git
cd Our.Umbraco.TrueCopy
dotnet run --project src/Cms

The first build takes a while: it restores NuGet packages and runs npm ci + npm run build for the backoffice client, because wwwroot/ is generated and not committed.

On first boot the site, unattended:

  1. creates src/Cms/umbraco/Data/Umbraco.sqlite.db,
  2. installs Umbraco and the admin user,
  3. runs the package migration,
  4. and imports every document type, data type, template and document from src/Cms/uSync/v18 (uSync:Settings:ImportOnFirstBoot).

Then open https://localhost:44366/umbraco and sign in:

   
Email hello@example.com
Password YVXVRvFlmUfwdGVWdprZWiZDKpwKiXfc

Those credentials, and the certificate password below, are in src/Cms/appsettings.json on purpose. This is a throwaway local harness with no data worth protecting — treat every secret in this repository as public, because it is.

Open the Content section, hover any document in the tree and click its … button: True Copy… sits just below Umbraco’s own Duplicate to….

The demo content

The demo content is a theme-park site, “Coaster World”: 67 documents.

The TrueCopy fixture is deliberate, and it is the only way to re-verify the rewriters. The Theme Park document type carries four properties named tc* — a Content Picker, a Multinode Treepicker, a Multi URL Picker and a Block List — plus a TrueCopy Test Block element type and two data types. None of them are used by any template; they exist purely to hold values that exercise every rewriter.

Alton Towers and Thorpe Park hold those values: links to each other (both inside United Kingdom, so a copy of that section repoints them), a link to Europa-Park in Germany (deliberately outside it, so the “still points at the original” report is never empty), a self-link nested inside a block, an external URL that must survive untouched, and a rich-text {localLink:…}.

To see the whole feature: open the … menu on United Kingdom → True Copy…, choose Europe as the destination, and turn Include descendants on. Leave descendants off and you copy a single page, which is a real operation but a dull demonstration.

Three optional steps

None of these are needed to run the site; each removes a specific annoyance.

1. Trust the development certificate

The site is served over HTTPS using a self-signed wildcard certificate for *.127.0.0.1.nip.io (committed at src/Cms/ssl/nip-io-wildcard.pfx — without it Kestrel will not start). Your browser will warn about it until you trust it:

# From an elevated PowerShell prompt
./src/Cms/ssl/Install-NipIoCertificate.ps1

The script adds only the public certificate to LocalMachine\Root, never the private key, and -Uninstall removes it again. Firefox needs security.enterprise_roots.enabled set to true to honour the machine store. New-NipIoCertificate.ps1 regenerates the certificate if you would rather not use the committed one.

Why nip.io rather than localhost: it resolves any *.127.0.0.1.nip.io name to loopback, which gives the harness real hostnames.

2. Set up the Umbraco MCP server

.mcp.json configures @umbraco-cms/mcp-dev, which lets an AI assistant read and change content through Umbraco’s management API. It needs credentials, and a fresh clone has none — uSync does not export users, so the API user does not exist in your new database.

  1. In the backoffice, go to Users → API Users and create one with access to the Content section.
  2. Open its profile and copy the client id and secret.
  3. cp .env.example .env and fill them in. .env is gitignored.

scripts/verify-upgrade.mjs reads the same file for its live checks.

3. Work on the backoffice client

cd src/TrueCopy/Client
npm install          # NOT --legacy-peer-deps
npm run watch        # rebuild on change
npm run build        # one-off
npm run generate-client   # regenerate src/api from the live OpenAPI document

Four things that will otherwise cost you an afternoon:

npm run generate-client reads https://localhost:44366/umbraco/openapi/truecopy.json, so the site has to be running. Run it whenever a controller signature or view model changes.

Building and testing

dotnet build Our.Umbraco.TrueCopy.slnx
dotnet test --solution Our.Umbraco.TrueCopy.slnx
dotnet format Our.Umbraco.TrueCopy.slnx --verify-no-changes

Three things about this that are not obvious:

Checking an Umbraco upgrade

node scripts/verify-upgrade.mjs              # static checks plus live probes
node scripts/verify-upgrade.mjs --static-only

It checks that the version pins agree across the host, the package and the client, then — with the site running — that the OpenAPI document generates, the App_Plugins bundle is served, and every parameterless endpoint answers. upgrading.md covers the half that needs judgement.

Contributing

Open an issue before a large change, so we can agree the shape of it first. For anything smaller: fork, branch, make sure dotnet build, dotnet format --verify-no-changes and dotnet test all pass, and open a pull request. CI runs exactly those.

New dependencies are worth a conversation: this package deliberately depends on nothing that is not published by Microsoft or Umbraco.