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:
co-authored by
Claude Opus 5
parent
3db9d4dfc6
commit
aeafe0af36
@@ -1,5 +1,9 @@
|
|||||||
<Solution>
|
<Solution>
|
||||||
<Folder Name="/Solution Items/">
|
<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=".editorconfig" />
|
||||||
<File Path=".gitattributes" />
|
<File Path=".gitattributes" />
|
||||||
<File Path=".gitignore" />
|
<File Path=".gitignore" />
|
||||||
|
|||||||
@@ -4,6 +4,14 @@
|
|||||||
|
|
||||||
## Команды
|
## Команды
|
||||||
|
|
||||||
|
Полный локальный гейт (формат → сборка → тесты) — то, что нужно прогнать перед коммитом:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./build.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
Отдельные шаги, если нужен только один из них:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dotnet build AvParser.slnx -c Release
|
dotnet build AvParser.slnx -c Release
|
||||||
```
|
```
|
||||||
@@ -14,9 +22,11 @@ dotnet test AvParser.slnx -c Release
|
|||||||
dotnet csharpier check .
|
dotnet csharpier check .
|
||||||
```
|
```
|
||||||
```bash
|
```bash
|
||||||
dotnet run --project src/AvParser.Desktop
|
./run.ps1
|
||||||
```
|
```
|
||||||
|
|
||||||
|
На Linux/macOS — `./build.sh` и `./run.sh` с теми же шагами.
|
||||||
|
|
||||||
## Слои
|
## Слои
|
||||||
|
|
||||||
`Core ← Infrastructure ← UI ← Desktop`, строго в одну сторону.
|
`Core ← Infrastructure ← UI ← Desktop`, строго в одну сторону.
|
||||||
@@ -95,6 +105,14 @@ dotnet run --project src/AvParser.Desktop
|
|||||||
открепленного контрола не строится визуальное дерево.
|
открепленного контрола не строится визуальное дерево.
|
||||||
- **csharpier — единственный владелец форматирования** (включая `.axaml` и `.csproj`).
|
- **csharpier — единственный владелец форматирования** (включая `.axaml` и `.csproj`).
|
||||||
`IDE0055` понижен до suggestion: два форматтера с `TreatWarningsAsErrors` дерутся насмерть.
|
`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()`), со своей
|
||||||
|
установкой. Зависимость намеренно не добавлена.
|
||||||
|
|
||||||
## Качество
|
## Качество
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
<ItemGroup Label="Avalonia">
|
<ItemGroup Label="Avalonia">
|
||||||
<PackageVersion Include="Avalonia" Version="$(AvaloniaVersion)" />
|
<PackageVersion Include="Avalonia" Version="$(AvaloniaVersion)" />
|
||||||
<PackageVersion Include="Avalonia.Desktop" Version="$(AvaloniaVersion)" />
|
<PackageVersion Include="Avalonia.Desktop" Version="$(AvaloniaVersion)" />
|
||||||
<PackageVersion Include="Avalonia.Diagnostics" Version="$(AvaloniaVersion)" />
|
|
||||||
<PackageVersion Include="Avalonia.Fonts.Inter" Version="$(AvaloniaVersion)" />
|
<PackageVersion Include="Avalonia.Fonts.Inter" Version="$(AvaloniaVersion)" />
|
||||||
<PackageVersion Include="Avalonia.Headless" Version="$(AvaloniaVersion)" />
|
<PackageVersion Include="Avalonia.Headless" Version="$(AvaloniaVersion)" />
|
||||||
<PackageVersion Include="Avalonia.Headless.XUnit" Version="$(AvaloniaVersion)" />
|
<PackageVersion Include="Avalonia.Headless.XUnit" Version="$(AvaloniaVersion)" />
|
||||||
|
|||||||
@@ -13,9 +13,31 @@ end-to-end до появления настоящей логики.
|
|||||||
|
|
||||||
Нужен .NET SDK **10.0.100** (закреплён в `global.json`).
|
Нужен .NET SDK **10.0.100** (закреплён в `global.json`).
|
||||||
|
|
||||||
|
Полный локальный гейт — восстановление, проверка форматирования, сборка, тесты:
|
||||||
|
|
||||||
```bash
|
```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
|
```bash
|
||||||
dotnet build AvParser.slnx -c Release
|
dotnet build AvParser.slnx -c Release
|
||||||
```
|
```
|
||||||
@@ -23,13 +45,7 @@ dotnet build AvParser.slnx -c Release
|
|||||||
dotnet test AvParser.slnx -c Release
|
dotnet test AvParser.slnx -c Release
|
||||||
```
|
```
|
||||||
```bash
|
```bash
|
||||||
dotnet run --project src/AvParser.Desktop
|
dotnet csharpier check .
|
||||||
```
|
|
||||||
|
|
||||||
Форматирование (csharpier — единственный владелец форматирования, включая `.axaml` и `.csproj`):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
dotnet tool restore && dotnet csharpier check .
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -117,7 +133,9 @@ Semi.Avalonia даёт темы контролов; токены — это се
|
|||||||
2. Переключить тему кнопкой в заголовке и в Settings; перезапустить — выбор сохранился.
|
2. Переключить тему кнопкой в заголовке и в Settings; перезапустить — выбор сохранился.
|
||||||
3. На странице Parse нажать **50k rows**, затем **Parse** — виден прогресс; **Cancel**
|
3. На странице Parse нажать **50k rows**, затем **Parse** — виден прогресс; **Cancel**
|
||||||
останавливает на середине и пишет, сколько успело разобраться.
|
останавливает на середине и пишет, сколько успело разобраться.
|
||||||
4. `F12` в Debug-сборке открывает Avalonia DevTools — там видно, как переключаются
|
В Avalonia 12 инспектора «из коробки» больше нет: `Avalonia.Diagnostics` остановился на 11.3.x,
|
||||||
`:compact` / `:medium` / `:expanded`.
|
а DevTools вынесли в отдельный инструмент со своей установкой
|
||||||
|
(`AvaloniaUI.DiagnosticsSupport` + `.WithDeveloperTools()`). Поэтому `F12` здесь ничего не
|
||||||
|
открывает — зависимость намеренно не добавлена.
|
||||||
|
|
||||||
Настройки и логи лежат в `%APPDATA%/AvParser` (Windows) или `~/.config/AvParser` (Linux/macOS).
|
Настройки и логи лежат в `%APPDATA%/AvParser` (Windows) или `~/.config/AvParser` (Linux/macOS).
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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'
|
||||||
@@ -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
|
||||||
@@ -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[@]}"
|
||||||
@@ -28,8 +28,13 @@
|
|||||||
<PackageReference Include="Serilog.Extensions.Logging" />
|
<PackageReference Include="Serilog.Extensions.Logging" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(Configuration)' == 'Debug'">
|
<!--
|
||||||
<!-- F12 opens DevTools, which is the fastest way to see :compact/:medium/:expanded toggle. -->
|
No Avalonia.Diagnostics here: it stops at 11.3.x and has no Avalonia 12 build. Avalonia 12
|
||||||
<PackageReference Include="Avalonia.Diagnostics" />
|
moved the inspector out of the framework into a separate tool with its own installation
|
||||||
</ItemGroup>
|
(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>
|
</Project>
|
||||||
|
|||||||
Reference in New Issue
Block a user