Skip to content

Build and Publish Template Package #15

Build and Publish Template Package

Build and Publish Template Package #15

name: Build and Publish Template Package
on:
workflow_dispatch:
inputs:
version:
description: 'Package version (e.g., 1.0.0)'
required: true
default: '1.0.0'
author:
description: 'Template author'
required: true
default: 'Nitin Singh'
description:
description: 'Template description'
required: true
default: 'Full-stack Clean Architecture template with .NET 9 API and Angular 19'
jobs:
build-and-publish:
runs-on: windows-latest
env:
TEMPLATE_VERSION: ${{ github.event.inputs.version }}
TEMPLATE_AUTHOR: ${{ github.event.inputs.author }}
TEMPLATE_DESCRIPTION: ${{ github.event.inputs.description }}
NUGET_AUTH_TOKEN: ${{ secrets.NUGET_API_KEY }}
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: '9.0.x'
- name: Setup NuGet
uses: NuGet/setup-nuget@v1
with:
nuget-version: '6.x'
- name: Display version
run: echo "Building template version ${{ env.TEMPLATE_VERSION }}"
- name: Create output directories
run: |
mkdir template-output
mkdir nupkg
- name: Run template preparation script
shell: pwsh
run: |
./template-src/CreateTemplate.ps1 -SourceDirectory . -TemplateNamespace Contact -OutputDirectory ./template-output
- name: Copy template.json file
shell: pwsh
run: |
# Create template config directory
New-Item -Path "./template-output/.template.config" -ItemType Directory -Force
# Copy the existing template.json file
Copy-Item -Path "./template-src/template.json" -Destination "./template-output/.template.config/template.json"
# Update metadata in template.json
$templateJsonPath = "./template-output/.template.config/template.json"
$templateJson = Get-Content -Path $templateJsonPath -Raw | ConvertFrom-Json
$templateJson.author = "${{ env.TEMPLATE_AUTHOR }}"
$templateJson | ConvertTo-Json -Depth 10 | Set-Content -Path $templateJsonPath
- name: Copy README template
shell: pwsh
run: |
# Ensure README is properly set up for NuGet package
Write-Host "Setting up README for NuGet package..."
# Check if README template exists in template-src
if (Test-Path "./template-src/README.template.md") {
# Copy as main README.md in the template output root
Copy-Item -Path "./template-src/README.template.md" -Destination "./template-output/README.md"
Write-Host "✅ README file set up successfully at template-output/README.md"
# Verify the file exists and has content
if (Test-Path "./template-output/README.md") {
$fileContent = Get-Content -Path "./template-output/README.md" -Raw
$contentLength = $fileContent.Length
Write-Host "README.md file size: $contentLength bytes"
} else {
Write-Error "README.md was not copied correctly"
}
} else {
Write-Error "README.template.md not found in template-src directory"
- name: Copy and update .nuspec file
shell: pwsh
run: |
# Copy the .nuspec file
Copy-Item -Path "./template-src/CleanArchitecture.FullStack.Template.nuspec" -Destination "./template-output/CleanArchitecture.FullStack.Template.nuspec"
# Update metadata in the .nuspec file
$nuspecPath = "./template-output/CleanArchitecture.FullStack.Template.nuspec"
$nuspecContent = Get-Content -Path $nuspecPath -Raw
# Replace version, author, and description with workflow inputs
$nuspecContent = $nuspecContent -replace '<version>.*?</version>', "<version>${{ env.TEMPLATE_VERSION }}</version>"
$nuspecContent = $nuspecContent -replace '<authors>.*?</authors>', "<authors>${{ env.TEMPLATE_AUTHOR }}</authors>"
$nuspecContent = $nuspecContent -replace '<description>.*?</description>', "<description>${{ env.TEMPLATE_DESCRIPTION }}</description>"
$nuspecContent = $nuspecContent -replace '<copyright>.*?</copyright>', "<copyright>Copyright © ${{ env.TEMPLATE_AUTHOR }} $((Get-Date).Year)</copyright>"
Set-Content -Path $nuspecPath -Value $nuspecContent
- name: Verify template structure
shell: pwsh
run: |
Write-Host "Verifying template structure..."
# Check that required files exist in template-output
$requiredFiles = @(
"docker-compose.yml",
".env-example",
"README.md"
)
foreach ($file in $requiredFiles) {
$filePath = "./template-output/$file"
if (Test-Path $filePath) {
Write-Host "✅ $file exists"
} else {
Write-Host "❌ $file is missing"
# Create empty file if missing to prevent template validation errors
New-Item -Path $filePath -ItemType File -Force
}
}
# Check that required directories exist
$requiredDirs = @(
"backend",
"frontend"
)
foreach ($dir in $requiredDirs) {
$dirPath = "./template-output/$dir"
if (Test-Path $dirPath) {
Write-Host "✅ $dir directory exists"
} else {
Write-Host "❌ $dir directory is missing"
# Create directory if missing
New-Item -Path $dirPath -ItemType Directory -Force
}
}
Write-Host "Template structure verification completed"
- name: Pack template
run: |
nuget pack ./template-output/CleanArchitecture.FullStack.Template.nuspec -OutputDirectory ./nupkg
- name: Verify template package contents
shell: pwsh
run: |
# Extract and verify the NuGet package contents to ensure README is included
$packagePath = Get-ChildItem -Path "./nupkg/*.nupkg" | Select-Object -First 1 -ExpandProperty FullName
$extractPath = "./nupkg-extracted"
Write-Host "Extracting NuGet package to verify contents..."
if (Test-Path $extractPath) {
Remove-Item -Path $extractPath -Recurse -Force
}
New-Item -Path $extractPath -ItemType Directory | Out-Null
# Extract the package using NuGet
nuget install CleanArchitecture.FullStack.Template -Source "./nupkg" -OutputDirectory $extractPath -ExcludeVersion
# Verify README exists
$readmePath = "$extractPath/CleanArchitecture.FullStack.Template/README.md"
if (Test-Path $readmePath) {
Write-Host "✅ README.md exists in the NuGet package"
} else {
Write-Error "❌ README.md is missing from the NuGet package"
- name: Test template package
run: |
dotnet new install ./nupkg/CleanArchitecture.FullStack.Template.${{ env.TEMPLATE_VERSION }}.nupkg
mkdir test-project
cd test-project
# Verify template installed correctly
dotnet new --list | findstr "cleanarch"
# Create project from template with parameters from template.json
# Test both with camelCase and PascalCase to verify both work
dotnet new cleanarch-fullstack --Organization TestCompany --ProjectName TestProject
# List files to verify creation
dir
- name: Test template package with organization
run: |
mkdir test-project-with-org
cd test-project-with-org
# Create project from template with organization parameter
dotnet new cleanarch-fullstack --organization TestCompany --projectName TestProjectWithOrg
# List files to verify creation
dir
- name: Test template package without organization
run: |
mkdir test-project-no-org
cd test-project-no-org
# Create project from template without organization parameter
dotnet new cleanarch-fullstack --projectName TestProjectNoOrg
# List files to verify creation
dir
- name: Test template package without Angular
run: |
mkdir test-project-no-angular
cd test-project-no-angular
# Create project from template without Angular
dotnet new cleanarch-fullstack --organization TestCompany --projectName TestProjectNoAngular --includeAngular false
# Verify that frontend directory is not created
if (Test-Path "./frontend") {
Write-Error "Frontend directory should not exist when includeAngular is false"
exit 1
}
# List files to verify creation
dir
- name: Test template package with default folder naming
shell: pwsh
run: |
# Test with organization - should create folder YourCompany.TestProject
dotnet new cleanarch-fullstack --Organization YourCompany --ProjectName TestProject
if (Test-Path "YourCompany.TestProject") {
Write-Host "✅ YourCompany.TestProject folder created successfully"
} else {
Write-Error "❌ YourCompany.TestProject folder not created"
exit 1
}
# Test without organization - should create folder TestProjectNoOrg
dotnet new cleanarch-fullstack --ProjectName TestProjectNoOrg
if (Test-Path "TestProjectNoOrg") {
Write-Host "✅ TestProjectNoOrg folder created successfully"
} else {
Write-Error "❌ TestProjectNoOrg folder not created"
exit 1
}
# Test with explicit output folder
dotnet new cleanarch-fullstack --Organization YourCompany --ProjectName TestProject -o CustomFolder
if (Test-Path "CustomFolder") {
Write-Host "✅ CustomFolder created successfully"
} else {
Write-Error "❌ CustomFolder not created"
exit 1
}
- name: Upload package artifact
uses: actions/upload-artifact@v4
with:
name: nuget-package
path: ./nupkg/*.nupkg
- name: Push to NuGet
run: |
dotnet nuget push ./nupkg/CleanArchitecture.FullStack.Template.${{ env.TEMPLATE_VERSION }}.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json