Rename your project
One dotnet new command renames the .NET 10 solution, namespaces, database, Keycloak realm and Aspire resources to your name, and leaves the Appointment domain untouched.
- You'll get
- A renamed copy that builds and passes tests
- Time
- A few minutes
- Touches
- Every project, plus config and infra names
- Before you start
- .NET 10 SDK; Docker and Node to run
Generate a renamed project
Section titled “Generate a renamed project”Install the template, run dotnet new appointme -n <Name>, and commit the output before you change anything.
-
Install the template. Pin the version so you can generate the same output again later.
Terminal window dotnet new install BravoDev.AppointMe.Templates::1.2.1 -
Generate.
-nis the only option the template takes. The output goes into a newContoso.Booking/folder. Pass-o <dir>to put it somewhere else.Terminal window dotnet new appointme -n Contoso.Booking -
Commit the untouched output. The template doesn’t create a git repository, and nothing in the generated files records which version made them. Commit right away and put the version in the message, because taking updates later starts from this commit.
Terminal window cd Contoso.Bookinggit initgit add -Agit commit -m "Generate from BravoDev.AppointMe.Templates 1.2.1" -
Build and test. Both pass on a fresh copy, with no code changes.
Terminal window dotnet build Contoso.Booking.slndotnet test Contoso.Booking.sln -
Run it. Trust the dev certificate once per machine, then start the Aspire host. The quick start covers what comes up and where.
Terminal window dotnet dev-certs https --trustcd src/Contoso.Booking.Aspire && dotnet run
What -n renames
Section titled “What -n renames”Every brand token becomes your name, in whichever form the place it sits in allows.
| What | In AppointMe | With -n Contoso.Booking |
Form |
|---|---|---|---|
| Namespaces, projects, folders, solution | AppointMe.Crm, AppointMe.sln |
Contoso.Booking.Crm, Contoso.Booking.sln |
As typed |
| SQL database | Database=AppointMe |
Database=Contoso.Booking |
As typed |
| Connection string name | AppointMeSql |
ContosoBookingSql |
Separators removed |
| Keycloak realm and its file | appointme, appointme-realm.json |
contoso.booking, contoso.booking-realm.json |
Lowercase |
| Cookies and the demo login | appointme.auth, demo@appointme.dev |
contoso.booking.auth, demo@contoso.booking.dev |
Lowercase |
| Aspire resources, Compose containers | appointme-sql, appointme-api |
contoso-booking-sql, contoso-booking-api |
Kebab-case |
Aspire project type in Program.cs |
AppointMe_Api |
Contoso_Booking_Api |
Underscores |
Azure names in infra/main.bicep |
acrappointme…, stappointme… |
acrcontosobooking…, stcontosobooking… |
Lowercase letters and digits |
Each form exists because the target rejects the others. Aspire resource names can’t contain dots or underscores (its ASPIRE006 check), C# identifiers can’t contain dots, and Azure registry and storage account names allow only lowercase letters and digits.
The template picks the form from the character that follows each match. This symbol handles the dotted form, and it fires only before one of nine characters:
"identityBucket": { "type": "derived", "valueSource": "name", "valueTransform": "identity", "replaces": "AppointMe", "fileRename": "AppointMe", "onlyIf": [ { "before": "." }, { "before": " " }, { "before": "\"" }, { "before": "'" }, { "before": "\n" }, { "before": "\r" }, { "before": ";" }, { "before": "-" }, { "before": "<" } ]},templates/README.md in the upstream repo lists every symbol and why it exists. If you add an identifier that contains AppointMe and the character after it isn’t on any list (AddAppointMeBilling is followed by B), the template won’t rename it.
What it leaves alone
Section titled “What it leaves alone”The lowercase brand token appointme is the first nine letters of appointment. A plain find-and-replace would also rewrite the Appointment aggregate, the /appointments route and the appointments.statistics:view permission.
So the template never matches the lowercase token on its own. It matches guarded forms such as appointme-, appointme. and appointme", and appointmen… matches none of them. The PascalCase AppointMe can’t collide, because Appointment has a lowercase m.
| Left as it is | Why |
|---|---|
Appointment, /appointments, appointments.statistics:view |
Domain vocabulary, not the brand. CI’s template smoke test counts every Appointment before and after the rename. |
Module names Identity, Organizations, Crm, Booking |
They aren’t brand tokens, so -n Contoso.Booking gives you Contoso.Booking.Booking. |
Company and Authors in Directory.Build.props |
Still BravoDev. Only the repository name inside RepositoryUrl changes. |
LICENSE |
MIT requires you to keep BravoDev’s copyright notice. Add your own line next to it. |
Password1 and the Keycloak client secrets in appsettings.Development.json |
Every generated project starts with the same local values. |
Fix the metadata and placeholders before you deploy: What to change first has the list.
Why is the name the only option?1 min · optional
The modules are wired together through events. Booking builds its own copies of CRM customers and of Organizations companies and employees from their domain events, so a switch that dropped a module would generate code that doesn’t compile.
With one parameter there’s one combination to test. .github/workflows/template.yml runs templates/smoke-test.sh on every pull request: it packs the template, generates Contoso.Booking, builds and tests it, lints its frontend and checks that no brand token is left. To drop a module, generate first and then follow Remove a module.
Naming rules for -n
Section titled “Naming rules for -n”Use a valid C# namespace such as Contoso.Booking. dotnet new accepts any name, so a bad one only shows up when you build.
| Name | What happens |
|---|---|
Contoso.Booking |
Builds. The documented form: company, then product. |
Contoso |
Builds. A single segment is a valid namespace too. |
my-booking-app |
dotnet new reports success. The build then fails with hundreds of CS1022 and CS0116 errors, because the code now says namespace my-booking-app.Shared…. |
- Each dot-separated segment must be a C# identifier: letters, digits and underscores, not starting with a digit. No hyphens and no spaces.
- Use PascalCase segments. Namespaces take the name exactly as typed, so
contoso.bookinggives you lowercase namespaces. - Keep it short if anyone builds on Windows without long-path support. The name appears in nearly every path, often twice:
src/CRM/Contoso.Booking.Crm/Contoso.Booking.Crm.csproj.
The generated README and demo login
Section titled “The generated README and demo login”A generated project gets its own README, written for your copy, with the demo login already renamed.
It comes from templates/overlay/README.md, not from the repo’s marketing README. It covers running the stack, the demo user, and a “Change these before deploying” list. The local Keycloak realm ships with a demo account, which Authentication:SuperAdmins in appsettings.Development.json makes a super admin. Its credentials follow your name:
| Demo login | Rule | For Contoso.Booking |
|---|---|---|
demo@<name, lowercased>.dev |
demo@contoso.booking.dev |
|
| Password | <name without separators>1 |
ContosoBooking1 |
To skip the login form, open https://localhost:5173/api/v1/login/demo. Demo:Enabled switches it. It’s on in appsettings.Development.json, and also in appsettings.Devtest.example.json.
The package also leaves out files that belong to upstream:
| Not in a generated project | Why |
|---|---|
CHANGELOG.md, the root README.md, docs/images/ |
Upstream release material |
src/AppointMe.Api/appsettings.Devtest.json |
BravoDev’s deployment config. You get appsettings.Devtest.example.json to fill in. |
templates/, .template.config/, .github/workflows/template.yml |
The packaging project, and the job that publishes BravoDev’s NuGet package |
.claude/settings.json, docs/superpowers/ |
Upstream’s own tooling settings and plans |
Everything else ships, renamed: CLAUDE.md and the agent skills, the Bicep files in infra/, and the devtest.yml, codeql.yml and secret-scan.yml workflows.
If you cloned instead
Section titled “If you cloned instead”There’s no rename script in the repo. To rename a clone, pack the template from it and generate from the package.
| You want | Do this |
|---|---|
| The easiest upstream merges | Keep the AppointMe names. A clone with upstream’s names can merge release tags directly. |
| Your own names, and you haven’t changed anything yet | Use dotnet new from nuget.org, as above |
| Your own names, keeping work you’ve already done in the clone | Pack the clone, then generate from the package |
-
Pack the template from the clone’s root. The package takes your working tree, minus build output and the withheld files above. Its version comes from
<Version>intemplates/AppointMe.Templates.csproj, which is still1.2.1on main.Terminal window dotnet pack templates/AppointMe.Templates.csproj -c Release -o ../appointme-pack -
Install the package and generate. If another copy of the template is installed, uninstall it first, because both use the same package id.
Terminal window dotnet new uninstall BravoDev.AppointMe.Templatesdotnet new install ../appointme-pack/BravoDev.AppointMe.Templates.1.2.1.nupkgdotnet new appointme -n Contoso.Booking -o ../Contoso.Booking -
Look for brand tokens the template didn’t know. These are the smoke test’s own leftover checks, and both should print nothing. A hit in your own code needs a rename by hand.
Terminal window cd ../Contoso.Bookinggrep -rIl "AppointMe" . --exclude-dir=node_modules --exclude-dir=obj --exclude-dir=bin --exclude-dir=distgrep -rIoE 'appointme([^n]|$)' . --exclude-dir=node_modules --exclude-dir=obj --exclude-dir=bin --exclude-dir=dist -
Start a new repository, and uninstall the template. The generated folder has no git history: your history stays in the clone. Commit the output as in step 3 at the top of this page, then run
dotnet new uninstall BravoDev.AppointMe.Templates.
Verify it works
Section titled “Verify it works”A good rename builds, passes its tests and leaves no brand token behind.
| Check | Command | Expect |
|---|---|---|
| The backend builds | dotnet build Contoso.Booking.sln |
0 Error(s) |
| The tests pass | dotnet test Contoso.Booking.sln |
No failures: 217 tests from 1.2.1, 221 from main |
| No brand token is left | The two grep commands above |
No output |
| The frontend passes its checks | npm ci && npm run check in src/Contoso.Booking.Frontend |
No errors |
| The stack starts | dotnet run in src/Contoso.Booking.Aspire |
You can sign in as demo@contoso.booking.dev |
Troubleshooting
Section titled “Troubleshooting”| Symptom | Cause | Fix |
|---|---|---|
Hundreds of CS1022 and CS0116 errors in a fresh project |
-n isn’t a valid namespace, for example my-booking-app |
Generate again with a dotted PascalCase name |
dotnet new doesn’t know appointme |
The template isn’t installed | dotnet new install BravoDev.AppointMe.Templates::1.2.1 |
The output contains .git, CHANGELOG.md or appsettings.Devtest.json |
You installed the clone folder, not a package | dotnet new uninstall <folder>, then pack and install the .nupkg |
AppointMe still appears in your own code |
The character after it isn’t one the template routes | Rename it by hand |
| Keycloak won’t import the realm after you renamed it by hand | Keycloak requires the file to be named <realm>-realm.json (“File name / realm name mismatch”) |
Keep the file name and its "realm" value in step |