Skip to content

polish: component quality bundle — DataTable server-side, TypedSelect… #74

polish: component quality bundle — DataTable server-side, TypedSelect…

polish: component quality bundle — DataTable server-side, TypedSelect… #74

Workflow file for this run

name: CI
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Cache NuGet packages
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/packages.lock.json') }}
restore-keys: |
${{ runner.os }}-nuget-
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
10.0.x
- name: Restore dependencies
run: dotnet restore ShellUI.slnx
# Generates src/ShellUI.Components/wwwroot/shellui-all.css fresh on every run.
# File is gitignored — the Windows + Linux Tailwind CLI binaries produce
# byte-different output, so drift-checking a committed file failed spuriously.
# Instead we regenerate and validate content properties.
- name: Build precompiled CSS bundle
# Invoke via `bash` (not `./script.sh`) so we don't rely on the file's
# executable bit. Windows contributors can edit and commit without
# remembering `git update-index --chmod=+x`.
run: |
set -euxo pipefail
bash scripts/rebuild-precompiled-css.sh
BUNDLE=src/ShellUI.Components/wwwroot/shellui-all.css
test -f "$BUNDLE" || (echo "ERROR: bundle was not produced"; exit 1)
# Content assertions using grep -F (fixed-string, no regex escaping headaches).
# These catch classes of bugs we've actually shipped before — worth the paranoia.
grep -Fq -- '--background' "$BUNDLE" || (echo "ERROR: theme vars not in bundle"; exit 1)
grep -Fq '.bg-background' "$BUNDLE" || (echo "ERROR: core utility rule missing"; exit 1)
grep -Fq 'hover\:bg-accent' "$BUNDLE" || (echo "ERROR: hover: modifier rules missing"; exit 1)
grep -Fq 'state=open' "$BUNDLE" || (echo "ERROR: data-[state=open] arbitrary value not compiled"; exit 1)
grep -Fq 'px-2\.5' "$BUNDLE" || (echo "ERROR: Badge padding class px-2.5 missing (variant .cs helpers not scanned?)"; exit 1)
grep -Fq 'border-transparent' "$BUNDLE" || (echo "ERROR: border-transparent missing (variant .cs helpers not scanned?)"; exit 1)
# Size guard — well above the ~77KB current 68 components emit, tight
# enough to catch a runaway (someone disabling minify, dumping the whole
# tailwind base without tree-shake, etc.).
size=$(wc -c < "$BUNDLE")
echo "precompiled bundle size: ${size} bytes"
if [ "$size" -gt 150000 ]; then
echo "ERROR: bundle exceeded 150KB — investigate before shipping."
exit 1
fi
- name: Build
run: dotnet build ShellUI.slnx --no-restore --configuration Release
- name: Run tests
run: dotnet test ShellUI.slnx --no-restore --no-build --configuration Release --verbosity normal
# End-to-end build of a scaffolded project — catches anything the in-process
# TemplateCompileTests miss (missing usings, dependency resolution).
- name: Smoke-test CLI scaffolding
shell: bash
run: |
set -euxo pipefail
TMPDIR=$(mktemp -d)
dotnet pack src/ShellUI.CLI/ShellUI.CLI.csproj -c Release -o "$TMPDIR/nupkgs" --no-build
dotnet tool install --tool-path "$TMPDIR/tools" --add-source "$TMPDIR/nupkgs" ShellUI.CLI --prerelease
export PATH="$TMPDIR/tools:$PATH"
mkdir -p "$TMPDIR/app" && cd "$TMPDIR/app"
dotnet new blazor -o SmokeApp --no-restore
cd SmokeApp
shellui init --tailwind standalone --yes
# Assert init produced a working host: App.razor patched with render mode,
# theme bootstrap, and shellui.js script tag.
grep -q 'HeadOutlet @rendermode="InteractiveServer"' Components/App.razor || (echo "init did not patch HeadOutlet @rendermode"; exit 1)
grep -q 'Routes @rendermode="InteractiveServer"' Components/App.razor || (echo "init did not patch Routes @rendermode"; exit 1)
grep -q 'ShellUI theme bootstrap' Components/App.razor || (echo "init did not inject theme bootstrap"; exit 1)
grep -q '<script src="shellui.js"></script>' Components/App.razor || (echo "init did not inject shellui.js script tag"; exit 1)
grep -q 'shellui-sidebar.js' Components/App.razor && (echo "init incorrectly injected shellui-sidebar.js script tag (sidebar JS is dynamically imported)"; exit 1) || true
# Assert input.css has the full theme, not just @import "tailwindcss";
grep -q '@theme inline' wwwroot/input.css || (echo "init did not write full theme to input.css"; exit 1)
grep -q ':root' wwwroot/input.css || (echo "init did not write :root variables to input.css"; exit 1)
grep -q '\.dark' wwwroot/input.css || (echo "init did not write .dark variables to input.css"; exit 1)
shellui add chart pie-chart dashboard-02 data-table --force
# NuGet dependencies (Blazor-ApexCharts, System.Linq.Dynamic.Core) should
# now be added automatically by `shellui add`. Assert they appear in the
# project file so a regression in the auto-install fails loudly here.
grep -q 'Blazor-ApexCharts' SmokeApp.csproj || (echo "shellui add chart did not add Blazor-ApexCharts NuGet dep"; exit 1)
grep -q 'System.Linq.Dynamic.Core' SmokeApp.csproj || (echo "shellui add data-table did not add System.Linq.Dynamic.Core NuGet dep"; exit 1)
# And the DataTable models file must land at Components/UI/Models/, not be missing.
test -f Components/UI/Models/DataTableModels.cs || (echo "shellui add data-table did not install data-table-models"; exit 1)
# chart-styles ships the CSS for the custom tooltip + ApexCharts chrome.
# Without it, hovering a chart shows invisible white-on-white text.
test -f wwwroot/css/charts.css || (echo "shellui add chart did not install chart-styles CSS"; exit 1)
grep -q '<link href="css/charts.css"' Components/App.razor || (echo "shellui add chart did not link charts.css in App.razor"; exit 1)
dotnet build -c Debug
# Pure-NuGet install path — `dotnet add package ShellUI.Components` without
# the CLI. Uses a one-off NuGet.config that whitelists ONLY the local feed,
# so a missing local package can't silently fall back to nuget.org and pull
# an older version (which would mask whether the current PR's package is
# correct). $GITHUB_WORKSPACE is absolute — relative paths from inside the
# temp consumer dir would resolve to the wrong root.
- name: NuGet-only install — verify safelist ships
shell: bash
run: |
set -euxo pipefail
LOCAL_FEED="$GITHUB_WORKSPACE/src/ShellUI.Components/bin/Release"
test -d "$LOCAL_FEED" || (echo "local feed not found at $LOCAL_FEED — did the pack step run?"; exit 1)
ls "$LOCAL_FEED"/*.nupkg | head -3
# DIAGNOSTIC: dump what's actually inside the fresh nupkg. The precompiled
# bundle must ship as a static web asset for consumers to `<link>` it.
echo "---nupkg contents (looking for shellui-all.css):---"
unzip -l "$LOCAL_FEED"/ShellUI.Components*.nupkg | grep -E 'shellui-all|staticwebassets/' | head -10 || echo " (no matches — bundle not in nupkg!)"
echo "---source wwwroot check:---"
ls -la "$GITHUB_WORKSPACE/src/ShellUI.Components/wwwroot/" | head -10
TMPDIR=$(mktemp -d)
mkdir -p "$TMPDIR/app" && cd "$TMPDIR/app"
dotnet new blazor -o NuGetSmoke --no-restore
cd NuGetSmoke
# Pin restore to only the local feed; nuget.org explicitly disabled so a
# broken local feed can't be papered over by a public-published older version.
cat > NuGet.config <<EOF
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="local" value="$LOCAL_FEED" />
</packageSources>
</configuration>
EOF
# Evict every cached ShellUI.Components from ~/.nuget/packages before restore.
# The runner-level cache is restored from actions/cache and may contain a
# stale copy of the current version from an earlier CI run (before we added
# the precompiled bundle, .cs scan, etc.). NuGet prefers cached over local
# feeds when version matches, so a stale cache silently masks the fresh
# nupkg. Wildcard covers any version — future-proof.
rm -rf ~/.nuget/packages/shellui.components
dotnet add package ShellUI.Components --prerelease
# The safelist gets copied into wwwroot/ during the consumer's build by
# the package's build/ShellUI.Components.targets — auto-imported by NuGet.
# Need to actually build the project, not just restore, for the Copy to run.
dotnet build -c Debug
SAFELIST="wwwroot/shellui-classes.txt"
test -f "$SAFELIST" || (
echo "ERROR: $SAFELIST not in consumer project after dotnet build."
echo "The package's build/ShellUI.Components.targets should have copied it."
echo "Files in wwwroot/:"; ls -la wwwroot/ 2>/dev/null || echo " (no wwwroot/ directory)"
exit 1
)
# Sanity: the safelist contains real Tailwind classes
grep -q 'bg-background' "$SAFELIST" || (echo "safelist appears malformed — missing core Tailwind class 'bg-background'"; exit 1)
wc -l "$SAFELIST"
# In .NET 10, MapStaticAssets() doesn't physically copy static web assets
# from referenced packages into the consumer's bin/ during `dotnet build`
# — it uses a manifest and reads from the extracted NuGet cache at runtime.
# So the correct assertion is "does the file exist in the extracted package
# cache after restore", which proves it'll be served at
# `_content/ShellUI.Components/shellui-all.css` in the running app.
BUNDLE="$(find ~/.nuget/packages/shellui.components -name 'shellui-all.css' -type f | head -1)"
if [ -z "$BUNDLE" ]; then
echo "ERROR: shellui-all.css not in consumer's extracted NuGet cache — the pure-NuGet install path is broken."
echo "---consumer's extracted NuGet cache tree:---"
find ~/.nuget/packages/shellui.components -type f 2>/dev/null | head -30
exit 1
fi
echo "found bundle at: $BUNDLE"
BUNDLE_SIZE=$(wc -c < "$BUNDLE")
echo "bundle size: $BUNDLE_SIZE bytes"
# Spot-check a handful of representative rules using fixed-string grep
# (no regex escaping headaches). These catch classes of bugs we've hit.
grep -Fq -- '--background' "$BUNDLE" || (echo "ERROR: bundle missing --background theme var"; exit 1)
grep -Fq '.bg-background' "$BUNDLE" || (echo "ERROR: bundle missing .bg-background utility"; exit 1)
grep -Fq 'hover\:bg-accent' "$BUNDLE" || (echo "ERROR: bundle missing hover:bg-accent modifier rule"; exit 1)
grep -Fq 'state=open' "$BUNDLE" || (echo "ERROR: bundle missing data-[state=open] arbitrary-value modifier"; exit 1)
grep -Fq 'px-2\.5' "$BUNDLE" || (echo "ERROR: bundle missing Badge padding — .cs variant scan regression?"; exit 1)
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: nuget-packages
path: |
**/*.nupkg
**/*.snupkg