|
| 1 | +# Job Template Zip Automation Plan |
| 2 | + |
| 3 | +## Problem |
| 4 | + |
| 5 | +The `BlazorDataOrchestrator.JobCreatorTemplate` project is shipped to users as a zip file located at `src/BlazorOrchestrator.Web/JobTemplate/BlazorDataOrchestrator.JobCreatorTemplate.zip`. Today this zip is created **manually** with error-prone steps: |
| 6 | + |
| 7 | +1. Copy the entire `src/BlazorDataOrchestrator.JobCreatorTemplate` directory |
| 8 | +2. Delete `bin/`, `obj/`, `Properties/` directories |
| 9 | +3. Delete `BlazorDataOrchestrator.JobCreatorTemplate.csproj.user` |
| 10 | +4. Include the root-level `JobTemplate.slnx` file |
| 11 | +5. Zip everything and place it in `src/BlazorOrchestrator.Web/JobTemplate/` |
| 12 | + |
| 13 | +This is tedious, easy to forget, and can lead to stale zip files being deployed. |
| 14 | + |
| 15 | +--- |
| 16 | + |
| 17 | +## Goals |
| 18 | + |
| 19 | +- **Zero manual steps** — the zip is regenerated automatically on every build of `BlazorOrchestrator.Web`. |
| 20 | +- **Always in sync** — any change to the template project is immediately reflected in the zip. |
| 21 | +- **No extra tools** — use only MSBuild targets and built-in .NET SDK capabilities (or a small PowerShell/shell script called from MSBuild). |
| 22 | +- **CI-friendly** — works in both local dev (`dotnet build` / `aspire run`) and CI/CD pipelines. |
| 23 | + |
| 24 | +--- |
| 25 | + |
| 26 | +## Proposed Solution |
| 27 | + |
| 28 | +### Option A — MSBuild Target in `BlazorOrchestrator.Web.csproj` (Recommended) |
| 29 | + |
| 30 | +Add a `<Target>` to `BlazorOrchestrator.Web.csproj` that runs **before** the build, invokes a PowerShell/shell script (or inline MSBuild tasks) to produce the zip, and places it in the `JobTemplate/` folder so it gets included as content. |
| 31 | + |
| 32 | +#### Implementation Steps |
| 33 | + |
| 34 | +##### 1. Create the packaging script |
| 35 | + |
| 36 | +Create `scripts/Package-JobTemplate.ps1`: |
| 37 | + |
| 38 | +```powershell |
| 39 | +<# |
| 40 | +.SYNOPSIS |
| 41 | + Packages the JobCreatorTemplate project into a zip for distribution. |
| 42 | +#> |
| 43 | +param( |
| 44 | + [Parameter(Mandatory)] |
| 45 | + [string]$SourceDir, # Path to src/BlazorDataOrchestrator.JobCreatorTemplate |
| 46 | +
|
| 47 | + [Parameter(Mandatory)] |
| 48 | + [string]$SlnxFile, # Path to JobTemplate.slnx |
| 49 | +
|
| 50 | + [Parameter(Mandatory)] |
| 51 | + [string]$OutputZip # Full path for the output .zip file |
| 52 | +) |
| 53 | +
|
| 54 | +$ErrorActionPreference = 'Stop' |
| 55 | +
|
| 56 | +# Create a temp staging directory |
| 57 | +$stagingDir = Join-Path ([System.IO.Path]::GetTempPath()) "JobTemplateStaging_$([guid]::NewGuid().ToString('N'))" |
| 58 | +$templateDir = Join-Path $stagingDir "BlazorDataOrchestrator.JobCreatorTemplate" |
| 59 | +
|
| 60 | +try { |
| 61 | + # Copy the template project to staging |
| 62 | + Copy-Item -Path $SourceDir -Destination $templateDir -Recurse |
| 63 | +
|
| 64 | + # Remove directories that should not be shipped |
| 65 | + $dirsToRemove = @('bin', 'obj', 'Properties') |
| 66 | + foreach ($dir in $dirsToRemove) { |
| 67 | + $target = Join-Path $templateDir $dir |
| 68 | + if (Test-Path $target) { |
| 69 | + Remove-Item $target -Recurse -Force |
| 70 | + } |
| 71 | + } |
| 72 | +
|
| 73 | + # Remove user-specific files |
| 74 | + $filesToRemove = @( |
| 75 | + '*.csproj.user', |
| 76 | + 'execution_errors.log' |
| 77 | + ) |
| 78 | + foreach ($pattern in $filesToRemove) { |
| 79 | + Get-ChildItem -Path $templateDir -Filter $pattern -Recurse | Remove-Item -Force |
| 80 | + } |
| 81 | +
|
| 82 | + # Copy the .slnx file into the staging root (sibling to the project folder) |
| 83 | + Copy-Item -Path $SlnxFile -Destination $stagingDir |
| 84 | +
|
| 85 | + # Ensure the output directory exists |
| 86 | + $outputDir = Split-Path $OutputZip -Parent |
| 87 | + if (-not (Test-Path $outputDir)) { |
| 88 | + New-Item -ItemType Directory -Path $outputDir -Force | Out-Null |
| 89 | + } |
| 90 | +
|
| 91 | + # Remove old zip if it exists |
| 92 | + if (Test-Path $OutputZip) { |
| 93 | + Remove-Item $OutputZip -Force |
| 94 | + } |
| 95 | +
|
| 96 | + # Create the zip |
| 97 | + Compress-Archive -Path "$stagingDir\*" -DestinationPath $OutputZip -Force |
| 98 | +
|
| 99 | + Write-Host "Created template zip: $OutputZip" |
| 100 | +} |
| 101 | +finally { |
| 102 | + # Clean up staging |
| 103 | + if (Test-Path $stagingDir) { |
| 104 | + Remove-Item $stagingDir -Recurse -Force |
| 105 | + } |
| 106 | +} |
| 107 | +``` |
| 108 | + |
| 109 | +##### 2. Add MSBuild target to `BlazorOrchestrator.Web.csproj` |
| 110 | + |
| 111 | +```xml |
| 112 | +<!-- Auto-package the JobCreatorTemplate zip before build --> |
| 113 | +<Target Name="PackageJobTemplate" BeforeTargets="BeforeBuild" |
| 114 | + Inputs="$(MSBuildThisFileDirectory)..\BlazorDataOrchestrator.JobCreatorTemplate\**\*.*" |
| 115 | + Outputs="$(MSBuildThisFileDirectory)JobTemplate\BlazorDataOrchestrator.JobCreatorTemplate.zip"> |
| 116 | + <Exec Command="pwsh -NoProfile -ExecutionPolicy Bypass -File "$(MSBuildThisFileDirectory)..\..\scripts\Package-JobTemplate.ps1" -SourceDir "$(MSBuildThisFileDirectory)..\BlazorDataOrchestrator.JobCreatorTemplate" -SlnxFile "$(MSBuildThisFileDirectory)..\..\JobTemplate.slnx" -OutputZip "$(MSBuildThisFileDirectory)JobTemplate\BlazorDataOrchestrator.JobCreatorTemplate.zip"" /> |
| 117 | +</Target> |
| 118 | +``` |
| 119 | + |
| 120 | +Key details: |
| 121 | +- **`Inputs` / `Outputs`** — MSBuild incremental build support. The target only re-runs when template source files are newer than the zip, avoiding unnecessary work on every build. |
| 122 | +- **`BeforeTargets="BeforeBuild"`** — ensures the zip is ready before the Web project compiles and copies content to output. |
| 123 | + |
| 124 | +##### 3. Ensure the zip is included as content |
| 125 | + |
| 126 | +Verify that `BlazorOrchestrator.Web.csproj` already includes the zip as content (it likely does via a glob). If not, add: |
| 127 | + |
| 128 | +```xml |
| 129 | +<ItemGroup> |
| 130 | + <Content Include="JobTemplate\BlazorDataOrchestrator.JobCreatorTemplate.zip"> |
| 131 | + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> |
| 132 | + </Content> |
| 133 | +</ItemGroup> |
| 134 | +``` |
| 135 | + |
| 136 | +##### 4. Add the zip to `.gitignore` |
| 137 | + |
| 138 | +Since the zip is now a build artifact, it should not be committed to source control: |
| 139 | + |
| 140 | +``` |
| 141 | +# Auto-generated job template zip |
| 142 | +src/BlazorOrchestrator.Web/JobTemplate/BlazorDataOrchestrator.JobCreatorTemplate.zip |
| 143 | +``` |
| 144 | + |
| 145 | +--- |
| 146 | + |
| 147 | +### Option B — Standalone Script Only (Simpler, Less Integrated) |
| 148 | + |
| 149 | +If MSBuild integration is not desired, the same `Package-JobTemplate.ps1` script can be run manually or as a CI step. This is simpler but requires developers to remember to run it. |
| 150 | + |
| 151 | +Usage: |
| 152 | +```powershell |
| 153 | +.\scripts\Package-JobTemplate.ps1 ` |
| 154 | + -SourceDir "src\BlazorDataOrchestrator.JobCreatorTemplate" ` |
| 155 | + -SlnxFile "JobTemplate.slnx" ` |
| 156 | + -OutputZip "src\BlazorOrchestrator.Web\JobTemplate\BlazorDataOrchestrator.JobCreatorTemplate.zip" |
| 157 | +``` |
| 158 | + |
| 159 | +--- |
| 160 | + |
| 161 | +### Option C — CI-Only Automation |
| 162 | + |
| 163 | +Add a step to the CI/CD pipeline (GitHub Actions / Azure DevOps) that runs the script before building the Web project. The zip remains committed in the repo for local dev but is always regenerated in CI. |
| 164 | + |
| 165 | +--- |
| 166 | + |
| 167 | +## Recommendation |
| 168 | + |
| 169 | +**Option A** is recommended because: |
| 170 | +- It is fully automatic — no manual steps, no forgotten updates. |
| 171 | +- Incremental builds prevent performance impact (the zip is only regenerated when template files change). |
| 172 | +- Works identically in local dev and CI. |
| 173 | +- The script is cross-platform compatible (PowerShell 7 / `pwsh` runs on Windows, Linux, macOS). |
| 174 | + |
| 175 | +--- |
| 176 | + |
| 177 | +## Files Changed |
| 178 | + |
| 179 | +| File | Action | Purpose | |
| 180 | +|------|--------|---------| |
| 181 | +| `scripts/Package-JobTemplate.ps1` | **Create** | Packaging script | |
| 182 | +| `src/BlazorOrchestrator.Web/BlazorOrchestrator.Web.csproj` | **Edit** | Add `PackageJobTemplate` MSBuild target | |
| 183 | +| `.gitignore` | **Edit** | Exclude the generated zip | |
| 184 | + |
| 185 | +## Risks & Mitigations |
| 186 | + |
| 187 | +| Risk | Mitigation | |
| 188 | +|------|------------| |
| 189 | +| `pwsh` not installed on build agent | .NET 10 SDK images include PowerShell 7. Add a check/fallback in CI. Alternatively, rewrite the script as a cross-platform shell script or pure MSBuild tasks. | |
| 190 | +| Incremental build `Inputs` glob is too broad | Refine the glob or add `Condition` checks if build times are affected. | |
| 191 | +| Zip contents diverge from expectations | Add a smoke test that extracts the zip and verifies expected files are present (e.g., `.csproj`, `Program.cs`, `.slnx`, no `bin/`). | |
0 commit comments