Add build and run scripts; drop the broken Avalonia.Diagnostics reference

A wrapper around `dotnet build` earns nothing, so build.ps1/build.sh are the
full local gate instead — tool restore, package restore, format check, build,
test — in the order that fails cheapest first, with a non-zero exit on failure.
run.ps1/run.sh default to Debug and pass extra arguments through to the app.
Both flavours ship because the Desktop head targets Windows, Linux and macOS.

Writing them immediately paid for itself: the very first run failed restore on
Avalonia.Diagnostics 12.1.1, which does not exist — the package stops at 11.3.x
because Avalonia 12 moved the inspector into a separate tool with its own
installation. The reference had survived because it sat behind
Condition="'$(Configuration)' == 'Debug'", and `dotnet restore` evaluates with
the default configuration while every build so far had passed -c Release. So
`dotnet build -c Release` worked and a bare `dotnet restore` did not.

Reference removed rather than replaced: AvaloniaUI.DiagnosticsSupport pulls in a
separately installed tool, which is not a dependency to add to a skeleton without
asking. README and CLAUDE.md no longer promise F12, and both traps are written
down where the next person will hit them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Leonid Pershin
2026-08-13 16:32:16 +03:00
co-authored by Claude Opus 5
parent 3db9d4dfc6
commit aeafe0af36
9 changed files with 306 additions and 16 deletions
+4
View File
@@ -1,5 +1,9 @@
<Solution>
<Folder Name="/Solution Items/">
<File Path="build.ps1" />
<File Path="build.sh" />
<File Path="run.ps1" />
<File Path="run.sh" />
<File Path=".editorconfig" />
<File Path=".gitattributes" />
<File Path=".gitignore" />
+19 -1
View File
@@ -4,6 +4,14 @@
## Команды
Полный локальный гейт (формат → сборка → тесты) — то, что нужно прогнать перед коммитом:
```bash
./build.ps1
```
Отдельные шаги, если нужен только один из них:
```bash
dotnet build AvParser.slnx -c Release
```
@@ -14,9 +22,11 @@ dotnet test AvParser.slnx -c Release
dotnet csharpier check .
```
```bash
dotnet run --project src/AvParser.Desktop
./run.ps1
```
На Linux/macOS — `./build.sh` и `./run.sh` с теми же шагами.
## Слои
`Core ← Infrastructure ← UI ← Desktop`, строго в одну сторону.
@@ -95,6 +105,14 @@ dotnet run --project src/AvParser.Desktop
открепленного контрола не строится визуальное дерево.
- **csharpier — единственный владелец форматирования** (включая `.axaml` и `.csproj`).
`IDE0055` понижен до suggestion: два форматтера с `TreatWarningsAsErrors` дерутся насмерть.
- **`Condition="'$(Configuration)' == 'Debug'"` на `PackageReference` — ловушка.**
`dotnet restore` вычисляется с конфигурацией по умолчанию, поэтому пакет обязан
резолвиться, даже если эту конфигурацию никто не собирает. Так тут проехал мёртвый
`Avalonia.Diagnostics` (его нет под Avalonia 12): `dotnet build -c Release` работал,
а голый `dotnet restore` падал.
- **Инспектора в Avalonia 12 нет из коробки.** `Avalonia.Diagnostics` закончился на 11.3.x;
DevTools живут отдельно (`AvaloniaUI.DiagnosticsSupport` + `.WithDeveloperTools()`), со своей
установкой. Зависимость намеренно не добавлена.
## Качество
-1
View File
@@ -15,7 +15,6 @@
<ItemGroup Label="Avalonia">
<PackageVersion Include="Avalonia" Version="$(AvaloniaVersion)" />
<PackageVersion Include="Avalonia.Desktop" Version="$(AvaloniaVersion)" />
<PackageVersion Include="Avalonia.Diagnostics" Version="$(AvaloniaVersion)" />
<PackageVersion Include="Avalonia.Fonts.Inter" Version="$(AvaloniaVersion)" />
<PackageVersion Include="Avalonia.Headless" Version="$(AvaloniaVersion)" />
<PackageVersion Include="Avalonia.Headless.XUnit" Version="$(AvaloniaVersion)" />
+28 -10
View File
@@ -13,9 +13,31 @@ end-to-end до появления настоящей логики.
Нужен .NET SDK **10.0.100** (закреплён в `global.json`).
Полный локальный гейт — восстановление, проверка форматирования, сборка, тесты:
```bash
dotnet restore AvParser.slnx
./build.ps1
```
Запуск приложения (по умолчанию Debug):
```bash
./run.ps1
```
На Linux и macOS — `./build.sh` и `./run.sh`, аргументы те же.
Полезные флаги:
```bash
./build.ps1 -Fix -Configuration Debug
```
`-Fix` переформатирует код вместо того, чтобы падать на непрошедшей проверке; `-SkipTests`
собирает без прогона тестов. То же в bash: `--fix`, `-c Debug`, `--skip-tests`.
Если нужны отдельные шаги, скрипты ничего не прячут:
```bash
dotnet build AvParser.slnx -c Release
```
@@ -23,13 +45,7 @@ dotnet build AvParser.slnx -c Release
dotnet test AvParser.slnx -c Release
```
```bash
dotnet run --project src/AvParser.Desktop
```
Форматирование (csharpier — единственный владелец форматирования, включая `.axaml` и `.csproj`):
```bash
dotnet tool restore && dotnet csharpier check .
dotnet csharpier check .
```
---
@@ -117,7 +133,9 @@ Semi.Avalonia даёт темы контролов; токены — это се
2. Переключить тему кнопкой в заголовке и в Settings; перезапустить — выбор сохранился.
3. На странице Parse нажать **50k rows**, затем **Parse** — виден прогресс; **Cancel**
останавливает на середине и пишет, сколько успело разобраться.
4. `F12` в Debug-сборке открывает Avalonia DevTools — там видно, как переключаются
`:compact` / `:medium` / `:expanded`.
В Avalonia 12 инспектора «из коробки» больше нет: `Avalonia.Diagnostics` остановился на 11.3.x,
а DevTools вынесли в отдельный инструмент со своей установкой
(`AvaloniaUI.DiagnosticsSupport` + `.WithDeveloperTools()`). Поэтому `F12` здесь ничего не
открывает — зависимость намеренно не добавлена.
Настройки и логи лежат в `%APPDATA%/AvParser` (Windows) или `~/.config/AvParser` (Linux/macOS).
+97
View File
@@ -0,0 +1,97 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Полный локальный гейт: формат, сборка, тесты.
.DESCRIPTION
То, что имеет смысл прогнать перед коммитом. Обёртка вокруг `dotnet build` сама по себе
бесполезна — ценность здесь в том, что шаги идут в правильном порядке, падают быстро и
возвращают ненулевой код возврата.
.PARAMETER Configuration
Debug или Release. По умолчанию Release — гейт должен проверять то, что уедет.
.PARAMETER Fix
Переформатировать код вместо проверки. Без этого флага несформатированный код валит сборку.
.PARAMETER SkipFormat
Пропустить проверку форматирования.
.PARAMETER SkipTests
Только собрать, не запускать тесты.
.EXAMPLE
./build.ps1
Формат, сборка и тесты в Release.
.EXAMPLE
./build.ps1 -Fix
Переформатировать код, затем собрать и прогнать тесты.
#>
[CmdletBinding()]
param(
[ValidateSet('Debug', 'Release')]
[string] $Configuration = 'Release',
[switch] $Fix,
[switch] $SkipFormat,
[switch] $SkipTests
)
$ErrorActionPreference = 'Stop'
Set-Location $PSScriptRoot
$solution = 'AvParser.slnx'
$started = Get-Date
function Invoke-Step {
param(
[Parameter(Mandatory)] [string] $Name,
[Parameter(Mandatory)] [scriptblock] $Action
)
Write-Host ''
Write-Host "==> $Name" -ForegroundColor Cyan
& $Action
# Native executables do not raise terminating errors, so the exit code is the only signal.
if ($LASTEXITCODE -ne 0) {
throw "$Name — код возврата $LASTEXITCODE"
}
}
try {
Invoke-Step 'Локальные инструменты' { dotnet tool restore }
Invoke-Step 'Восстановление пакетов' { dotnet restore $solution }
if (-not $SkipFormat) {
if ($Fix) {
Invoke-Step 'Форматирование (csharpier format)' { dotnet csharpier format . }
}
else {
# Before the build: an unformatted file is the cheapest possible failure.
Invoke-Step 'Проверка форматирования (csharpier check)' { dotnet csharpier check . }
}
}
Invoke-Step "Сборка ($Configuration)" {
dotnet build $solution -c $Configuration --no-restore --nologo
}
if (-not $SkipTests) {
Invoke-Step 'Тесты' {
dotnet test $solution -c $Configuration --no-build --nologo
}
}
$elapsed = (Get-Date) - $started
Write-Host ''
Write-Host ("OK — {0:mm\:ss}" -f $elapsed) -ForegroundColor Green
}
catch {
Write-Host ''
Write-Host "СБОЙ: $_" -ForegroundColor Red
exit 1
}
Executable
+66
View File
@@ -0,0 +1,66 @@
#!/usr/bin/env bash
# Полный локальный гейт: формат, сборка, тесты.
#
# Обёртка вокруг `dotnet build` сама по себе бесполезна — ценность в том, что шаги идут в
# правильном порядке, падают быстро и возвращают ненулевой код возврата.
#
# ./build.sh формат, сборка и тесты в Release
# ./build.sh --fix переформатировать код, затем собрать и прогнать тесты
# ./build.sh -c Debug другая конфигурация
# ./build.sh --skip-tests только собрать
set -euo pipefail
cd "$(dirname "$0")"
SOLUTION=AvParser.slnx
CONFIGURATION=Release
FIX=0
SKIP_FORMAT=0
SKIP_TESTS=0
while [ $# -gt 0 ]; do
case "$1" in
-c|--configuration) CONFIGURATION="$2"; shift 2 ;;
--fix) FIX=1; shift ;;
--skip-format) SKIP_FORMAT=1; shift ;;
--skip-tests) SKIP_TESTS=1; shift ;;
-h|--help) sed -n '2,10p' "$0"; exit 0 ;;
*) echo "Неизвестный аргумент: $1" >&2; exit 2 ;;
esac
done
case "$CONFIGURATION" in
Debug|Release) ;;
*) echo "Конфигурация должна быть Debug или Release, получено: $CONFIGURATION" >&2; exit 2 ;;
esac
step() { printf '\n\033[36m==> %s\033[0m\n' "$1"; }
trap 'printf "\n\033[31mСБОЙ на шаге: %s\033[0m\n" "${CURRENT_STEP:-?}" >&2' ERR
CURRENT_STEP='Локальные инструменты'; step "$CURRENT_STEP"
dotnet tool restore
CURRENT_STEP='Восстановление пакетов'; step "$CURRENT_STEP"
dotnet restore "$SOLUTION"
if [ "$SKIP_FORMAT" -eq 0 ]; then
if [ "$FIX" -eq 1 ]; then
CURRENT_STEP='Форматирование (csharpier format)'; step "$CURRENT_STEP"
dotnet csharpier format .
else
# До сборки: несформатированный файл — самый дешёвый повод упасть.
CURRENT_STEP='Проверка форматирования (csharpier check)'; step "$CURRENT_STEP"
dotnet csharpier check .
fi
fi
CURRENT_STEP="Сборка ($CONFIGURATION)"; step "$CURRENT_STEP"
dotnet build "$SOLUTION" -c "$CONFIGURATION" --no-restore --nologo
if [ "$SKIP_TESTS" -eq 0 ]; then
CURRENT_STEP='Тесты'; step "$CURRENT_STEP"
dotnet test "$SOLUTION" -c "$CONFIGURATION" --no-build --nologo
fi
printf '\n\033[32mOK\033[0m\n'
+50
View File
@@ -0,0 +1,50 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Запускает desktop-приложение.
.DESCRIPTION
По умолчанию Debug: быстрее собирается и даёт осмысленные стеки. Инспектора в Avalonia 12
из коробки нет — см. README.
.PARAMETER Configuration
Debug или Release. По умолчанию Debug.
.PARAMETER NoBuild
Не пересобирать — запустить то, что уже собрано.
.PARAMETER AppArgs
Всё остальное уходит в приложение как аргументы командной строки.
.EXAMPLE
./run.ps1
.EXAMPLE
./run.ps1 -Configuration Release -NoBuild
#>
[CmdletBinding()]
param(
[ValidateSet('Debug', 'Release')]
[string] $Configuration = 'Debug',
[switch] $NoBuild,
[Parameter(ValueFromRemainingArguments = $true)]
[string[]] $AppArgs = @()
)
$ErrorActionPreference = 'Stop'
Set-Location $PSScriptRoot
$arguments = @('run', '--project', 'src/AvParser.Desktop', '-c', $Configuration)
if ($NoBuild) {
$arguments += '--no-build'
}
if ($AppArgs.Count -gt 0) {
$arguments += '--'
$arguments += $AppArgs
}
Write-Host "==> dotnet $($arguments -join ' ')" -ForegroundColor Cyan
& dotnet @arguments
exit $LASTEXITCODE
Executable
+33
View File
@@ -0,0 +1,33 @@
#!/usr/bin/env bash
# Запускает desktop-приложение.
#
# По умолчанию Debug: быстрее собирается и даёт осмысленные стеки. Инспектора в Avalonia 12
# из коробки нет — см. README.
#
# ./run.sh Debug
# ./run.sh -c Release --no-build запустить уже собранное
# ./run.sh -- --какой-то-аргумент передать аргументы приложению
set -euo pipefail
cd "$(dirname "$0")"
CONFIGURATION=Debug
NO_BUILD=0
APP_ARGS=()
while [ $# -gt 0 ]; do
case "$1" in
-c|--configuration) CONFIGURATION="$2"; shift 2 ;;
--no-build) NO_BUILD=1; shift ;;
-h|--help) sed -n '2,9p' "$0"; exit 0 ;;
--) shift; APP_ARGS=("$@"); break ;;
*) echo "Неизвестный аргумент: $1 (аргументы приложения — после --)" >&2; exit 2 ;;
esac
done
ARGS=(run --project src/AvParser.Desktop -c "$CONFIGURATION")
[ "$NO_BUILD" -eq 1 ] && ARGS+=(--no-build)
[ ${#APP_ARGS[@]} -gt 0 ] && ARGS+=(-- "${APP_ARGS[@]}")
printf '\033[36m==> dotnet %s\033[0m\n' "${ARGS[*]}"
exec dotnet "${ARGS[@]}"
+9 -4
View File
@@ -28,8 +28,13 @@
<PackageReference Include="Serilog.Extensions.Logging" />
</ItemGroup>
<ItemGroup Condition="'$(Configuration)' == 'Debug'">
<!-- F12 opens DevTools, which is the fastest way to see :compact/:medium/:expanded toggle. -->
<PackageReference Include="Avalonia.Diagnostics" />
</ItemGroup>
<!--
No Avalonia.Diagnostics here: it stops at 11.3.x and has no Avalonia 12 build. Avalonia 12
moved the inspector out of the framework into a separate tool with its own installation
(AvaloniaUI.DiagnosticsSupport + `.WithDeveloperTools()`), so there is no built-in F12.
Note also that Condition="'$(Configuration)' == 'Debug'" on a PackageReference is a trap:
`dotnet restore` evaluates with the default configuration, so the package still has to
resolve even if nothing ever builds that configuration.
-->
</Project>