feat(charts): the three chart tools become embeddable, feature-gated … #30
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Proves the `dotnet new daxalgo-strategy` template scaffolds a plugin that BUILDS, TESTS, and PACKS — | |
| # both variants (headless + --ui). Two jobs: | |
| # | |
| # * in-repo — installs the template from THIS checkout, checks the template's pinned SDK version | |
| # tracks the repo's DaxAlgoSdkVersion (the anti-drift guard — a version bump that | |
| # forgets the template fails here), then scaffolds + builds + tests + packs both | |
| # variants and asserts the identity rule (no host DLLs in the plugin output). Every PR. | |
| # * published — installs `DaxAlgo.Templates` from NuGet.org and scaffolds against the PUBLISHED | |
| # packages with NO repo checkout (pure NuGet resolution). Gated behind schedule + | |
| # manual dispatch so a version-bump PR doesn't fail here before the version is live. | |
| name: template-smoke | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| schedule: | |
| - cron: '0 6 * * 1' # Mondays 06:00 UTC — catch a broken published chain | |
| workflow_dispatch: | |
| env: | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true | |
| jobs: | |
| # ── Anti-drift: the in-repo template must scaffold + build + test + pack, both variants ──────────── | |
| in-repo: | |
| runs-on: windows-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: '9.0.x' | |
| - name: AI context pack + template CLAUDE/AGENTS are regenerated (no drift) | |
| shell: pwsh | |
| run: | | |
| pwsh build/gen-ai-context.ps1 | |
| $generated = @( | |
| 'sdk/ai-context/daxalgo-strategy-context.md', | |
| 'templates/content/daxalgo-strategy/CLAUDE.md', | |
| 'templates/content/daxalgo-strategy/AGENTS.md' | |
| ) | |
| $diff = git status --porcelain -- $generated | |
| if ($diff) { git --no-pager diff -- $generated; throw "AI context is stale - run 'pwsh build/gen-ai-context.ps1' and commit." } | |
| Write-Host "AI context pack + template CLAUDE.md/AGENTS.md are current." | |
| - name: Template pins the current SDK version | |
| shell: pwsh | |
| run: | | |
| # A fresh scaffold must reference a PUBLISHED SDK, and that version must track the repo's | |
| # SdkInfo.Version / DaxAlgoSdkVersion — otherwise a bump silently orphans the template. | |
| $ver = (Select-String -Path Directory.Build.props -Pattern '<DaxAlgoSdkVersion>([^<]+)</DaxAlgoSdkVersion>').Matches[0].Groups[1].Value | |
| $csproj = 'templates/content/daxalgo-strategy/DaxNewStrategy/DaxNewStrategy.csproj' | |
| $pinned = Select-String -Path $csproj -Pattern 'Include="DaxAlgo\.Sdk(?:\.Wpf)?" Version="([^"]+)"' -AllMatches | | |
| ForEach-Object { $_.Matches } | ForEach-Object { $_.Groups[1].Value } | Sort-Object -Unique | |
| if ($pinned -contains $ver -and $pinned.Count -eq 1) { Write-Host "Template pins $ver (matches DaxAlgoSdkVersion)." } | |
| else { throw "Template SDK pin(s) [$($pinned -join ', ')] != DaxAlgoSdkVersion ($ver). Bump the template's PackageReference versions." } | |
| - name: Install the template from source | |
| shell: pwsh | |
| run: dotnet new install ./templates/content/daxalgo-strategy --force | |
| - name: Scaffold + build + test + pack (headless and --ui) | |
| shell: pwsh | |
| run: | | |
| $work = "$env:RUNNER_TEMP/scaffold" | |
| New-Item -ItemType Directory -Force $work | Out-Null | |
| foreach ($variant in @(@{ name = 'HeadlessPlug'; ui = $false }, @{ name = 'UiPlug'; ui = $true })) { | |
| $out = Join-Path $work $variant.name | |
| $newArgs = @('new', 'daxalgo-strategy', '-n', $variant.name, '-o', $out) | |
| if ($variant.ui) { $newArgs += '--ui' } | |
| & dotnet @newArgs; if ($LASTEXITCODE -ne 0) { throw "scaffold failed: $($variant.name)" } | |
| $sln = Get-ChildItem -Recurse $out -Filter '*.slnx' | Select-Object -First 1 | |
| dotnet build $sln.FullName -c Release; if ($LASTEXITCODE -ne 0) { throw "build failed: $($variant.name)" } | |
| dotnet test $sln.FullName -c Release --no-build; if ($LASTEXITCODE -ne 0) { throw "test failed: $($variant.name)" } | |
| $pack = Get-ChildItem -Recurse $out -Filter 'pack-plugin.ps1' | Select-Object -First 1 | |
| & $pack.FullName -Configuration Release; if ($LASTEXITCODE -ne 0) { throw "pack failed: $($variant.name)" } | |
| if (-not (Get-ChildItem -Recurse $out -Filter '*.daxplugin')) { throw "no .daxplugin produced: $($variant.name)" } | |
| # Identity rule: the plugin's build output must ship NONE of the host's contract assemblies. | |
| $bin = Get-ChildItem -Recurse $out -Directory | Where-Object { $_.FullName -match 'bin\\Release\\net9.0-windows7.0$' } | Select-Object -First 1 | |
| $leaked = Get-ChildItem $bin.FullName -Filter *.dll | Where-Object { $_.Name -match '^(TradingTerminal|DaxAlgo|MahApps|ControlzEx|ScottPlot)' } | |
| if ($leaked) { throw "identity rule broken - host DLLs in $($variant.name) output: $($leaked.Name -join ', ')" } | |
| Write-Host "$($variant.name): scaffolded, built, tested, packed; bin is clean." | |
| } | |
| # ── CLI: the daxalgo strategy tool drives scaffold -> build -> test -> package with the fake provider ─ | |
| cli: | |
| runs-on: windows-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: '9.0.x' | |
| - name: Install the template from source | |
| shell: pwsh | |
| run: dotnet new install ./templates/content/daxalgo-strategy --force | |
| - name: daxalgo strategy ai --provider fake (scaffold -> build -> test -> package) | |
| shell: pwsh | |
| run: | | |
| $tool = 'src/windows/Tools/DaxAlgo.StrategyTool/DaxAlgo.StrategyTool.csproj' | |
| $work = "$env:RUNNER_TEMP/cli" | |
| New-Item -ItemType Directory -Force $work | Out-Null | |
| Push-Location $work | |
| try { | |
| dotnet run --project "$env:GITHUB_WORKSPACE/$tool" -- strategy ai --name CliDemo --provider fake | |
| if ($LASTEXITCODE -ne 0) { throw 'daxalgo strategy ai --provider fake failed' } | |
| if (-not (Get-ChildItem -Recurse -Filter '*.daxplugin')) { throw 'no .daxplugin produced' } | |
| Write-Host 'CLI ai --provider fake: scaffold -> build -> test -> package OK.' | |
| } finally { Pop-Location } | |
| # ── Published: scaffold from NuGet.org with no repo checkout (pure package resolution) ───────────── | |
| published: | |
| if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' | |
| runs-on: windows-latest | |
| steps: | |
| - uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: '9.0.x' | |
| - name: Install the template from NuGet.org | |
| shell: pwsh | |
| run: dotnet new install DaxAlgo.Templates --force | |
| - name: Scaffold + build + test against the published packages (headless and --ui) | |
| shell: pwsh | |
| run: | | |
| $work = "$env:RUNNER_TEMP/scaffold" | |
| New-Item -ItemType Directory -Force $work | Out-Null | |
| foreach ($variant in @(@{ name = 'HeadlessPlug'; ui = $false }, @{ name = 'UiPlug'; ui = $true })) { | |
| $out = Join-Path $work $variant.name | |
| $newArgs = @('new', 'daxalgo-strategy', '-n', $variant.name, '-o', $out) | |
| if ($variant.ui) { $newArgs += '--ui' } | |
| & dotnet @newArgs; if ($LASTEXITCODE -ne 0) { throw "scaffold failed: $($variant.name)" } | |
| $sln = Get-ChildItem -Recurse $out -Filter '*.slnx' | Select-Object -First 1 | |
| dotnet build $sln.FullName -c Release; if ($LASTEXITCODE -ne 0) { throw "build failed: $($variant.name)" } | |
| dotnet test $sln.FullName -c Release --no-build; if ($LASTEXITCODE -ne 0) { throw "test failed: $($variant.name)" } | |
| Write-Host "$($variant.name): built + tested against the published SDK." | |
| } |