- Jun 20, 2024
Create a Template from your dotnet Projects
How do you make sure you are using the best practices when you're starting a new dotnet project? How do you get all the small parts you've developed over time and want to reuse, without copy-pasting code between projects.
Creating a template from your existing dotnet projects is really easy to do and to automate, and that's what I'll show you today.
Basic structure
To get started, get the Microsoft template authoring package by running:
dotnet new install Microsoft.TemplateEngine.Authoring.TemplatesNow, create a root folder for all the files we're about to create and add a content folder inside. The name of this folder will be used later when creating a package for the templates.
In that content folder, create a folder for you template and you can name it whatever you want. I'm creating a template for a project that I can reuse it as a starter kit, so I'll name it starterkit.
The next step is to add the files that you want to include in your template. I want to reuse the project I built in my previous blog post, so I copied it there. It's a simple Blazor project with a customized Bootstrap for now, but I will be adding more features that I extract from other projects.
Creating the template
To turn this into a template, create a .template.config folder with a template.json file inside, using
dotnet new install template.jsonHere's the structure you should have right now. Of course, the actual project inside starterkit will be different in your case.
I won't go into the details of the template.json file, as Microsoft already publishes extensive documentation at this repo. The key property is the shortName, as that's what you'll use when creating new projects. Also, it's worth noting that whatever you set as sourceName will be replaced in your template with what the user inputs in the --name options when creating.
{
"$schema": "http://json.schemastore.org/template",
"author": "Pedro Sampaio",
"classifications": [
"Web"
],
"identity": "DeployBit.StarterKit",
"name": "DeployBit: Starter Kit",
"shortName": "deploybit-starterkit",
"sourceName": "MyApplication",
"tags": {
"language": "C#",
"type": "project"
},
"primaryOutputs": [
{
"path": "src/MyApplication.WebApp/MyApplication.WebApp.csproj"
},
{
"path": "src/MyApplication.WebApp.Client/MyApplication.WebApp.Client.csproj"
}
],
"postActions": [
{
"actionId": "210D431B-A78B-4D2F-B762-4ED3E3EA9025",
"description": "Restore NuGet packages required by this project.",
"manualInstructions": [
{
"text": "Run 'dotnet restore'"
}
],
"continueOnError": true
}
]
}Test locally
You now have all the pieces in place to use your template, so run
dotnet new install ./ in the root folder to add this template to dotnet new.
You can now create new projects using this template. My preferred way to test this is to create a playground folder (and add it to .gitignore). Then, I can run:
dotnet new deploybit-starterkit -n MyNewApplication -o MyNewApplicationIf you go into that folder, you should have your new project and the initial MyApplication has been replaced with MyNewApplication. If the project you used as a base was running correctly, you can test everything is working with dotnet run.
Now, uninstall everything with dotnet new uninstall ./. You can remove the stuff inside playground or you can use different names when creating new files for testing.
Packaging
After making sure the actual template is working and the output is correct, it's time to package it properly. This means creating a .csproj file for the template pack, which you do running the following command in the root folder:
dotnet new templatepack -n "DeployBit.Templates"This file has some standard properties for template, but there are a couple of important sections. The first one is at the top, where you define your template:
<PropertyGroup>
<PackageId>DeployBit.Templates</PackageId>
<Title>DeployBit Templates</Title>
<Authors>Pedro Sampaio</Authors>
<Description>TODO: fill the package description here</Description>
<PackageTags>TODO: fill the tags here</PackageTags>
<PackageProjectUrl>TODO: include a link to an associated project, repository, or company website</PackageProjectUrl>
</PropertyGroup>The second one is closer to the bottom, where you specify what to include in your template:
<ItemGroup>
<Content Include="content\**\*" Exclude="content\**\bin\**;content\**\obj\**" />
<Compile Remove="**\*" />
<None Include="README.md" Pack="true" PackagePath="" />
</ItemGroup>If you used some folder name other than content, you should change it here also.
You can now run dotnet pack and install the package again with
dotnet new install bin/Release/DeployBit.Templates.1.0.0.nupkgGo back to the playground folder and test again.
If everything is working, you can uninstall again, although this time you'll need to use the package name: dotnet new uninstall DeployBit.Templates
Deploying (using GitHub)
However, packaging properly doesn't mean a thing, if you can't distribute it. And this is the same process as you would do for any library that you publish.
Install MinVer by adding this to the template .csproj file:
<ItemGroup>
<PackageReference Include="MinVer" Version="5.*">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>I also like to change the name of the preview packages:
<PropertyGroup>
<MinVerDefaultPreReleaseIdentifiers>preview</MinVerDefaultPreReleaseIdentifiers>
</PropertyGroup>With these changes in place, you can tag this version using git tag 0.0.1.
When you run dotnet pack again, a new package is created that you can install by running:
dotnet new install bin/Release/DeployBit.Templates.0.0.1.nupkgYour package can also have a preview version, if you have any commits after the tag.
The last step is to setup the GitHub action. For that, create a build.yml in the .github/workflows folder with the following content:
name: Pack and deploy templates
on:
push:
branches: [ "main" ]
jobs:
pack:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Restore dependencies
run: dotnet restore
- name: Pack
run: dotnet pack -o:package
- name: Push generated package to GitHub registry
run: dotnet nuget push ./package/*.nupkg --source https://nuget.pkg.github.com/deploybitio/index.json --api-key ${{ github.token }}This should be relatively simple. Just restore the dependencies (for MinVer), pack the template to the packages folder and push the package to GitHub packages. Adjust the push command if you want to send the packages to Nuget.
The only thing worth noting is that we fetch the repo with a depth of 0, to make sure we get the correct information to generate the version properly.
To make changes to the template, you work like in any other application: make changes to the files in the content folder and just push them (tagging before, if needed).