From abfab2568545160c7b6cc8f7fbc2bece730309fa Mon Sep 17 00:00:00 2001 From: gramps Date: Sun, 1 Nov 2026 07:01:50 -0800 Subject: [PATCH] build: the exe is LlamaCasty.exe, and the publish script proves the artifact MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Renames AssemblyName to LlamaCasty before the first Steam depot — Steamworks is explicit that renaming a live exe is painful, so this had to land first, not later. Four assembly-qualified pack URIs carried the old name, not three: MainWindow (window icon), OverlayHost (logo), RenameRecordingDialog and KeepBroadcastDialog. Pack URIs resolve by assembly name at runtime, so a stale one compiles, passes every test and then silently renders nothing the first time that view opens. AssemblyIdentityTests guards both halves — the assembly name, and no source XAML still naming the legacy assembly. Packaging/Package.appxmanifest and package-msix.sh carry the exe name too. scripts/publish.sh replaces the by-hand publish: single-file win-x64, ReadyToRun, ffmpeg beside the exe, and it refuses to finish if ffmpeg, LICENSE, PRIVACY.md or THIRD-PARTY-NOTICES.txt are missing, or if a test assembly leaked in. That last guard exists because the first publish was wrong in a way no test could see: PublishSingleFile + IncludeNativeLibrariesForSelfExtract bundled the libav*.dll family into the exe — a 9-file output with no ffmpeg folder and a 182 MB binary, so the locator would have found nothing at runtime. ExcludeFromSingleFile="true" on the content keeps the folder a folder. Diffing against a control publish without the single-file flags found it; guessing at item globs wasted two attempts first (recorded in MyMistakes.md). Measured: 127 MB exe + 129 MB ffmpeg = 257 MB depot. Tests: AssemblyIdentity, StorePackaging, BuildStamp, BundledFonts all pass; app builds with 0 warnings. --- Controls/OverlayHost.xaml | 2 +- Distribution.md | 10 ++-- HANDOFF.md | 41 +++++++++------- KeepBroadcastDialog.xaml | 2 +- MainWindow.xaml | 2 +- MyMistakes.md | 29 +++++++++++ Packaging/Package.appxmanifest | 2 +- RenameRecordingDialog.xaml | 2 +- TASKS.md | 26 +++++----- docs/ai/product.md | 22 +++++---- scripts/package-msix.sh | 2 +- scripts/publish.sh | 62 +++++++++++++++++++++++ ytLive.Tests/AssemblyIdentityTests.cs | 71 +++++++++++++++++++++++++++ ytLive.Tests/StorePackagingTests.cs | 2 +- ytLive.csproj | 19 ++++++- 15 files changed, 240 insertions(+), 54 deletions(-) create mode 100644 scripts/publish.sh create mode 100644 ytLive.Tests/AssemblyIdentityTests.cs diff --git a/Controls/OverlayHost.xaml b/Controls/OverlayHost.xaml index ec8a2bb..62c4e73 100644 --- a/Controls/OverlayHost.xaml +++ b/Controls/OverlayHost.xaml @@ -156,7 +156,7 @@ - diff --git a/Distribution.md b/Distribution.md index 449f56f..ed4d48d 100644 --- a/Distribution.md +++ b/Distribution.md @@ -28,9 +28,13 @@ dotnet publish ytLive.csproj -c Release -r win-x64 --self-contained true \ read DLC ownership. Its absence must degrade to the free profile, never to a crash. - **WebView2 is the one machine dependency.** Valve's Common Redistributables configure the Evergreen bootstrapper as a prerequisite, so the depot declares it instead of us shipping it. -- **`AssemblyName` is still `ytLive`** → the exe is `ytLive.exe` today. The rename to - `LlamaCasty.exe` is queued (three pack URIs break — see `product.md`), and **must land before the - first depot upload**, because Steam's docs are explicit that renaming a live exe is painful. +- **`AssemblyName` is `LlamaCasty`** (renamed 2026-11-01, before the first depot because + Steamworks warns about renaming a live exe) → the artifact is `LlamaCasty.exe` plus its + `ffmpeg\` folder. `scripts/publish.sh` stages both and refuses to finish if either is missing. +- **⛔ `ExcludeFromSingleFile="true"` on the ffmpeg content.** Without it, `PublishSingleFile` + + `IncludeNativeLibrariesForSelfExtract` swallow the libav\*.dll family **into the exe** — a + 182 MB binary with no `ffmpeg\` folder beside it, so the locator finds nothing and every stream + fails at runtime. The publish output is ~257 MB: 127 MB exe + 129 MB ffmpeg. - **Portable mode** (`portable.txt` → `.\data\`) survives as a feature for a copied install; it is not a delivery vector. diff --git a/HANDOFF.md b/HANDOFF.md index 62a4e99..19ed74b 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -1,35 +1,40 @@ -# HANDOFF — 2026-10-31 (Steam is the only channel; the site says so) +# HANDOFF — 2026-11-01 (the exe is LlamaCasty.exe; the depot folder builds and verifies) ## State -- Branch `main`, level with `origin` through `57ef9e7`; this unit is local. -- **Distribution is Steam-exclusive** (creator ruling 2026-10-31): a free app on Steam with the paid - edition as a **Steam DLC**; the **$10/month** plan is sold off-Steam by Polar and *stated, never - sold*, inside a Steam build. Winget, the direct zip, the Store and the certificate that served - them are retired. `commercial.md` → Channels owns policy, `Distribution.md` the depot mechanics, - `decisions.md` the cause of death. -- **Entitlement is `steamDlcOwned || polarKeyValid`**, both resolving to one `OutputProfile`. - `steam_api64.dll` ships beside the exe; the app must start without Steam and fall back to free. -- **Site updated and deployed** (`a9d07ce`): "Coming soon to Steam" ×6, zero Microsoft references, - privacy page says Steam bills the paid edition. The CTA gains a wishlist button only when the - store page is public. -- `www.llamacasty.com` resolves and 301s to the apex. Recipe in `MyMistakes.md`. +- Branch `main`, level with `origin` through `8f79b20`; this unit (the artifact rename) is local. +- **The shipped exe is `LlamaCasty.exe`.** `AssemblyName` renamed; four pack URIs fixed (the docs + said three — `KeepBroadcastDialog.xaml:5` was the fourth); manifest, MSIX script and + `StorePackagingTests` follow. Guarded by `ytLive.Tests/AssemblyIdentityTests.cs`. +- **`scripts/publish.sh`** stages `artifacts/steam-stage` and refuses to finish if `ffmpeg\`, the + licence, privacy or notices file is missing, or test assemblies leaked in. Output 257 MB. +- **Distribution is Steam-exclusive** (creator ruling 2026-10-31): free app + paid edition as a + Steam **DLC**; the **$10/month** plan sold off-Steam by Polar and *stated, never sold*, in-app. + `commercial.md` → Channels is policy; `Distribution.md` is depot mechanics. +- **Entitlement is `steamDlcOwned || polarKeyValid`**; `steam_api64.dll` ships beside the exe and + the app must start without Steam, falling back to free. +- **Site is live with Steam** (`a9d07ce`). `www.llamacasty.com` 301s to the apex. ## Next action -1. **Steamworks onboarding** (creator is in it now): NDA → identity → company details → tax - interview → payout bank → **$100 Steam Direct** → create the base app, note the App ID. -2. Then the surge slice: artifact shape (rename + publish + `steam_api64.dll`) → OutputProfile gate - + TASK 53 geometry tests → entitlement seam → licence UI + a Steam-less launch test. +1. **OutputProfile gate** — ≤921,600 px / ≤30 fps for the stream **and** the recorder, greyed tier + entries, plus the TASK 53 geometry tests that pin every tier. +2. Then the **entitlement seam** (`IEntitlement`, Steam DLC path behind a fake, Polar path) and the + licence UI. +3. Steamworks: App ID waiting on identity verification; then depot, store page (public 2 weeks + before release) and the DLC app. ## Landmines +- **⛔ `ExcludeFromSingleFile="true"` on the ffmpeg content** — without it the publisher bundles + libav\*.dll into the exe and the app ships unable to find ffmpeg. Recipe in `MyMistakes.md`. - **Nothing may gate a feature.** The only paid delta is the output profile; `ShowBrandFlash` stays `false` and entitlement must never enable it. - **No checkout inside a Steam build** — the Wallet rule. The monthly is named, never sold, in-app. - **The org access token never ships.** `organization_id` is public; the token is not. - **The site publishes the model, never the numbers**, and never takes payment or links a checkout. - **No self-updater and no version banner**: Steam updates the app. -- Pre-existing: the **full test suite hangs** (WASAPI teardown). Per-class runs are the way. +- Pre-existing: the **full test suite hangs** (WASAPI teardown) — per-class runs only; and + `ytLive.Tests` carries 3 xUnit analyzer warnings the app-only gate does not see. - Not every hex is a theme colour — `BorderColorPalette`/chat defaults are USER DATA; on-stream graphics come from `OutputPalette`. A red settings control is still a bug. diff --git a/KeepBroadcastDialog.xaml b/KeepBroadcastDialog.xaml index 17ba354..d6fdd38 100644 --- a/KeepBroadcastDialog.xaml +++ b/KeepBroadcastDialog.xaml @@ -2,7 +2,7 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Keep this broadcast?" Height="240" Width="460" - Icon="/ytLive;component/Assets/llamacasty-icon.png" + Icon="/LlamaCasty;component/Assets/llamacasty-icon.png" WindowStartupLocation="CenterOwner" ResizeMode="NoResize" ShowInTaskbar="False" Background="{StaticResource Brush.Background}"> diff --git a/MainWindow.xaml b/MainWindow.xaml index 74c86c9..9d77e86 100644 --- a/MainWindow.xaml +++ b/MainWindow.xaml @@ -11,7 +11,7 @@ mc:Ignorable="d" Title="{Binding WindowTitle}" Height="1040" Width="1920" MinWidth="1366" MinHeight="768" - Icon="/ytLive;component/Assets/llamacasty-icon.png" + Icon="/LlamaCasty;component/Assets/llamacasty-icon.png" Background="{StaticResource Brush.Background}" FontFamily="{StaticResource FontFamily.Sans}" WindowStartupLocation="Manual" diff --git a/MyMistakes.md b/MyMistakes.md index 53ec724..51f6cf1 100644 --- a/MyMistakes.md +++ b/MyMistakes.md @@ -166,6 +166,35 @@ in Studio → Settings → Channel → **Feature eligibility**: *"Advanced YouTube features aren't available for this account"*. If ID verification is ever wanted on a personal channel, do it **before** converting to a brand account. +### THE SINGLE-FILE PUBLISHER EATS ffmpeg (2026-11-01) + +**Problem:** `dotnet publish` with `PublishSingleFile=true` + +`IncludeNativeLibrariesForSelfExtract=true` produced a **9-file** output with **no `ffmpeg\` +folder** and a **182 MB exe** — the libav\*.dll family had been bundled *inside* the binary. The +build was green, the tests passed, and the app would have shipped unable to find ffmpeg at +runtime (the locator looks for `ffmpeg\ffmpeg.exe` beside the exe). + +**Diagnosis that worked:** diff the publish output against a **control publish with the +single-file flags removed** — ffmpeg was there. Then add flags back one at a time; +`IncludeNativeLibrariesForSelfExtract` was the one that broke it. + +**Fix:** mark the content `ExcludeFromSingleFile="true"`. + +```xml + +``` + +After: 127 MB exe, `ffmpeg\` present, depot ≈ 257 MB total. + +- ⚠️ **The `` before it is not the fix** — it silences a duplicate-item + warning from the SDK's default glob, and I tried it first and wrongly believed it had worked. + Two failed remedies in a row is the research trigger: read the publisher's own metadata + (`ExcludeFromSingleFile`) instead of guessing at item globs. +- ⚠️ **A clean build and green tests cannot see this.** Only the artifact can, which is why + `scripts/publish.sh` fails loudly when `ffmpeg\`, `LICENSE`, `PRIVACY.md` or + `THIRD-PARTY-NOTICES.txt` are missing, and when test assemblies leak in. + ### STEAM IS THE DISTRIBUTOR — AND WHAT IT DOES NOT ALLOW (2026-10-31) **Why recorded:** the distribution answer changed three times in one day (winget → zip+GitHub → diff --git a/Packaging/Package.appxmanifest b/Packaging/Package.appxmanifest index 62dbdbc..504f17f 100644 --- a/Packaging/Package.appxmanifest +++ b/Packaging/Package.appxmanifest @@ -50,7 +50,7 @@ diff --git a/RenameRecordingDialog.xaml b/RenameRecordingDialog.xaml index 15d9202..b610aff 100644 --- a/RenameRecordingDialog.xaml +++ b/RenameRecordingDialog.xaml @@ -2,7 +2,7 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Rename Recording" Height="304" Width="460" - Icon="/ytLive;component/Assets/llamacasty-icon.png" + Icon="/LlamaCasty;component/Assets/llamacasty-icon.png" WindowStartupLocation="CenterOwner" ResizeMode="NoResize" ShowInTaskbar="False" Background="{StaticResource Brush.Background}"> diff --git a/TASKS.md b/TASKS.md index 530274e..e201ccf 100644 --- a/TASKS.md +++ b/TASKS.md @@ -7,22 +7,18 @@ ## Next up -**The surge week (4 days, in this order).** Not every row below — this slice only, because it is -what the first public release is waiting on: +**The surge week (3 days, in this order).** Not every row below — this slice only, because it is +what the first public release is waiting on. Day 1 (artifact shape) is **done**: the exe is +`LlamaCasty.exe` and `scripts/publish.sh` stages the depot folder. -1. **Artifact shape** — `LlamaCasty.exe` rename (three pack URIs), `dotnet publish` profile with - `steam_api64.dll` beside the exe. Ends with a build that starts standalone and falls back to the - free profile. -2. **OutputProfile gate** — ≤921,600 px / ≤30 fps for the stream **and** the recorder, greyed tier +1. **OutputProfile gate** — ≤921,600 px / ≤30 fps for the stream **and** the recorder, greyed tier entries, plus the TASK 53 geometry tests that pin every tier. -3. **Entitlement seam** — `IEntitlement` with the Steam DLC path and a fake for tests, Polar's - customer-portal path for the monthly, `--activate` parsing, system messages. -4. **Real-machine day** — licence UI skeleton, a Steam-less launch proving the free profile, a test - broadcast. +2. Then the **entitlement seam** (`IEntitlement`, Steam DLC path behind a fake, Polar path) and the + licence UI. Detail: [`commercial.md`](docs/ai/commercial.md) → Output profile / Entitlement; -[`Distribution.md`](Distribution.md). Day 5, if the four go well: the visibility picker (a hard -prerequisite for any *public* build). +[`Distribution.md`](Distribution.md). After that: the visibility picker (a hard prerequisite for +any *public* build). **TASK 3·17 — the Text layer** stays the first product row after the surge. The `TextOverlay` source type exists as a placeholder and nothing else does: `AddSource` creates it 0×0, all four compositor @@ -80,9 +76,9 @@ Dog test. `UpdateBroadcast` in the same unit (it is the only code that can change a broadcast's privacy). - **Kill the dev multi-instance affordance** — `Helpers/InstanceProfile.cs` is already `#if DEBUG`; re-prove it on a Release build. -- **Release engineering** — `scripts/publish.sh` (self-contained win-x64, no test artifacts), - Debug-only `InternalsVisibleTo`, the `LlamaCasty.exe` assembly rename (three pack URIs, incl. - `MainWindow.xaml:14`), full license texts beside `THIRD-PARTY-NOTICES.txt` (TASK 4 req 9). +- **Release engineering** — `scripts/publish.sh` **done** (self-contained win-x64, `ffmpeg\` + guarded, no test artifacts), `LlamaCasty.exe` assembly rename **done**, full license texts beside + `THIRD-PARTY-NOTICES.txt` (TASK 4 req 9). Outstanding: Debug-only `InternalsVisibleTo`. - **Gold pass** (TASK 36): the screens/layers audit, native-Windows verification, and the branding-flash decision. diff --git a/docs/ai/product.md b/docs/ai/product.md index d01c1ea..5facfab 100644 --- a/docs/ai/product.md +++ b/docs/ai/product.md @@ -28,18 +28,22 @@ the internal repo/assembly/namespace name only. Exact current split: | surface | name | note | |---|---|---| | product / site / marketing | **LlamaCasty** | llamacasty.com, llamachile.tube | -| repo dir, csproj, `RootNamespace` | `ytLive` | 230 files: `namespace ytLive.*` | -| `AssemblyName` → shipped exe | `ytLive` | `ytLive.exe` | +| repo dir, csproj file, `RootNamespace` | `ytLive` | 230+ files: `namespace ytLive.*` | +| `AssemblyName` → shipped exe | **`LlamaCasty`** | `LlamaCasty.exe` — renamed 2026-11-01 | | app data dir | `%APPDATA%\ytLlive` | **capital L**, drifts from `ytLive` | -| `Distribution.md` tells customers | `LlamaCasty.exe` | ⚠ **mismatch vs the real `ytLive.exe`** | **Consequences, so nobody re-hunts them:** -- Renaming `` to `LlamaCasty` is the cheap win (the customer-visible exe - name matches what `Distribution.md` already promises; namespaces can stay `ytLive.*`). - It is **4 lines**, but it hard-breaks three pack URIs that spell the assembly name: - `MainWindow.xaml:14` (the **window icon** — taskbar/Alt-Tab), `OverlayHost.xaml:96`, - `RenameRecordingDialog.xaml:5`. Miss those and you ship a broken icon. Queued in TASKS.md - under the shipping task, not done. +- The assembly rename landed 2026-11-01 (before the first Steam depot, because Steamworks warns + that renaming a live exe is painful). Namespaces stay `ytLive.*` — they are not user-visible + and renaming them would churn every file for nothing. +- An assembly-qualified pack URI spells the assembly name, so the rename **hard-breaks silently** + at runtime, not at compile time. Four carried it, not three: + `MainWindow.xaml:14` (window icon — taskbar/Alt-Tab), `OverlayHost.xaml:96`, + `RenameRecordingDialog.xaml:5`, and `KeepBroadcastDialog.xaml:5`. + `ytLive.Tests/AssemblyIdentityTests.cs` now guards both halves: the assembly name is + `LlamaCasty` and no source XAML still names the legacy assembly. +- `Packaging/Package.appxmanifest` (`Executable`) and `scripts/package-msix.sh` carry the exe + name too; the MSIX is unused but must not lie about the artifact. - Renaming the `%APPDATA%\ytLlive` folder is **breaking** — it would orphan every existing user's layout DB, `Assets` and settings. Never do it casually; needs a migration. - No code compares its own assembly name (no `GetExecutingAssembly().GetName().Name` checks), diff --git a/scripts/package-msix.sh b/scripts/package-msix.sh index 1019434..1ba78fa 100644 --- a/scripts/package-msix.sh +++ b/scripts/package-msix.sh @@ -39,7 +39,7 @@ mkdir -p "$STAGE_WSL" --nologo -v minimal \ -o "$(wslpath -w "$STAGE_WSL")" -[ -f "$STAGE_WSL/ytLive.exe" ] || { echo "publish produced no ytLive.exe" >&2; exit 1; } +[ -f "$STAGE_WSL/LlamaCasty.exe" ] || { echo "publish produced no LlamaCasty.exe" >&2; exit 1; } [ -d "$STAGE_WSL/ffmpeg" ] || { echo "ffmpeg/ is missing from the package — it must ship bundled (Store policy 10.2.2)." >&2 exit 1 diff --git a/scripts/publish.sh b/scripts/publish.sh new file mode 100644 index 0000000..dd4943b --- /dev/null +++ b/scripts/publish.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +# publish.sh — build the artifact that goes into the Steam depot. +# Usage: ./scripts/publish.sh [output-dir] (default: artifacts/steam-stage) +# +# Why a script: the flags below are load-bearing and easy to lose. PublishTrimmed stays OFF +# (WPF resolves XAML by reflection), the single-file bundle must extract natives or ffmpeg- +# adjacent loading breaks, and ReadyToRun is what keeps startup under a second on a cold +# machine. Getting one of them wrong does not fail the build — it fails on a user's PC. +# +# The output is a folder, not a zip: steamcmd uploads a directory into a depot, and Steam +# delivers it. There is no installer, no admin prompt and no self-updater (Steam updates). +# +# Requires WINDOWS dotnet — see docs/ai/environment.md -> Run. + +set -euo pipefail + +DOTNET="/mnt/c/Program Files/dotnet/dotnet.exe" +[ -x "$DOTNET" ] || { echo "❌ Windows dotnet not found at $DOTNET"; exit 1; } + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +OUT_REL="${1:-artifacts/steam-stage}" +OUT="$ROOT/$OUT_REL" + +echo "══ publish ══" +rm -rf "$OUT" +mkdir -p "$OUT" + +"$DOTNET" publish "$(wslpath -w "$ROOT/ytLive.csproj")" \ + -c Release -r win-x64 --self-contained true \ + -p:PublishSingleFile=true \ + -p:IncludeNativeLibrariesForSelfExtract=true \ + -p:EnableCompressionInSingleFile=true \ + -p:PublishReadyToRun=true \ + -p:DebugType=none \ + --nologo -v minimal \ + -o "$(wslpath -w "$OUT")" + +echo "══ verify the staged artifact ══" +fail() { echo "❌ $1" >&2; exit 1; } + +[ -f "$OUT/LlamaCasty.exe" ] || fail "publish produced no LlamaCasty.exe (AssemblyName reverted?)" +[ -d "$OUT/ffmpeg" ] || fail "ffmpeg/ is missing — it must ship beside the exe (docs/ai/output.md)" +[ -f "$OUT/LICENSE" ] || fail "LICENSE is missing from the artifact" +[ -f "$OUT/PRIVACY.md" ] || fail "PRIVACY.md is missing from the artifact" +[ -f "$OUT/THIRD-PARTY-NOTICES.txt" ] || fail "THIRD-PARTY-NOTICES.txt is missing from the artifact" + +# Test artifacts must never reach a depot: a shipped xunit host is a support ticket. +if compgen -G "$OUT/*.Tests.dll" > /dev/null || compgen -G "$OUT/xunit*" > /dev/null; then + fail "test assemblies leaked into the publish output" +fi + +# steam_api64.dll arrives with the Steamworks SDK; until then its absence is expected and the +# app must fall back to the free profile, so this is a notice, not a failure. +if [ ! -f "$OUT/steam_api64.dll" ]; then + echo "⚠ steam_api64.dll not staged — the build will run as the free profile only." +fi + +echo "" +du -sh "$OUT" | awk '{print " artifact size: " $1}' +echo "✓ staged: $OUT_REL" +echo "" +echo "Next: steamcmd +run_app_build scripts/app_build_.vdf" diff --git a/ytLive.Tests/AssemblyIdentityTests.cs b/ytLive.Tests/AssemblyIdentityTests.cs new file mode 100644 index 0000000..f5b1c71 --- /dev/null +++ b/ytLive.Tests/AssemblyIdentityTests.cs @@ -0,0 +1,71 @@ +using System; +using System.IO; +using System.Linq; +using Xunit; + +namespace ytLive.Tests; + +/// +/// The shipped binary is LlamaCasty.exe (renamed 2026-11-01, before the first Steam +/// depot). Two things have to agree for that to be true, and neither fails the build when it +/// doesn't: +/// +/// +/// The assembly name comes from ytLive.csproj and silently reverting it produces +/// ytLive.exe again — which is why the rename lands before a depot upload rather than +/// after (Steamworks: renaming a live exe is painful). +/// Assembly-qualified WPF pack URIs (/Assembly;component/path) resolve by that +/// same name. A stale /ytLive;component/… compiles, runs, and then fails silently +/// the moment that particular view opens — a blank window icon, a missing logo in the overlay +/// host. Nobody notices until a dialog is opened at the wrong time, so the rename is exactly the +/// kind of change that needs a source-level guard. +/// +/// +/// The C# namespaces stay ytLive: they are not user-visible, and the file churn +/// would buy nothing. Only the assembly name and the URIs that quote it move. +/// +public class AssemblyIdentityTests +{ + private const string ShipName = "LlamaCasty"; + private const string LegacyName = "ytLive"; + + [Fact] + public void The_assembly_is_LlamaCasty_and_no_pack_uri_still_names_the_legacy_assembly() + { + Assert.Equal(ShipName, typeof(MainWindow).Assembly.GetName().Name); + + var root = RepoRoot(); + var stale = Directory.EnumerateFiles(root, "*.xaml", SearchOption.AllDirectories) + .Where(path => !IsBuildOutput(path)) + .Where(path => File.ReadAllText(path).Contains("/" + LegacyName + ";component/", StringComparison.Ordinal)) + .Select(path => Path.GetRelativePath(root, path)) + .ToArray(); + + Assert.True(stale.Length == 0, + "These XAML files still point at the legacy assembly name in a pack URI, which " + + "fails silently at runtime: " + string.Join(", ", stale)); + } + + /// Walks up from the test output directory to the repo root (the folder holding + /// ytLive.csproj). + private static string RepoRoot() + { + var dir = new DirectoryInfo(AppContext.BaseDirectory); + while (dir != null) + { + if (File.Exists(Path.Combine(dir.FullName, "ytLive.csproj"))) return dir.FullName; + dir = dir.Parent; + } + throw new InvalidOperationException("Could not locate the repo root from " + AppContext.BaseDirectory); + } + + /// Publish and compile output carry copies of the XAML (and old generated XAML); + /// only the source tree is the thing under test. + private static bool IsBuildOutput(string path) + { + var parts = path.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + return parts.Any(part => part.Equals("bin", StringComparison.OrdinalIgnoreCase) + || part.Equals("obj", StringComparison.OrdinalIgnoreCase) + || part.Equals("artifacts", StringComparison.OrdinalIgnoreCase)); + } +} diff --git a/ytLive.Tests/StorePackagingTests.cs b/ytLive.Tests/StorePackagingTests.cs index 5cc7288..af59ff5 100644 --- a/ytLive.Tests/StorePackagingTests.cs +++ b/ytLive.Tests/StorePackagingTests.cs @@ -97,7 +97,7 @@ public class StorePackagingTests var application = package.Element(Foundation + "Applications")! .Element(Foundation + "Application")!; Assert.Equal("Windows.FullTrustApplication", (string?)application.Attribute("EntryPoint")); - Assert.Equal("ytLive.exe", (string?)application.Attribute("Executable")); + Assert.Equal("LlamaCasty.exe", (string?)application.Attribute("Executable")); Assert.Equal("mediumIL", (string?)application.Attribute(Uap10 + "TrustLevel")); Assert.Equal("packagedClassicApp", (string?)application.Attribute(Uap10 + "RuntimeBehavior")); diff --git a/ytLive.csproj b/ytLive.csproj index 406ec2d..6052d53 100644 --- a/ytLive.csproj +++ b/ytLive.csproj @@ -8,7 +8,10 @@ true true Assets\llamacasty-icon.ico - ytLive + + LlamaCasty ytLive - + + + +