Задания, отчёты и патчи, лежавшие в C:\Users\Ochenstarik\projects и в домашней папке, перенесены в agents/. Разложено по агентам там, где имя файла позволяло определить автора; остальное — в _salvage-2026-08-18/ и разбирается вручную. Патчи в notes/salvage-2026-08-18/ — незакоммиченная работа из брошенных рабочих копий: она существовала только на диске. Тяжёлое (релизные архивы, инсталляторы, наборы данных) в репозиторий не попало: оно лежит рядом, в Agent_projects/_archive и Agent_projects/_data. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
125 lines
10 KiB
Markdown
125 lines
10 KiB
Markdown
# Воспроизводимость сборки: централизованные версии пакетов и lock-файлы
|
||
|
||
## Часть 1 — сначала исправить PR #14
|
||
|
||
Не начинать новую ветку, пока не закрыты два дефекта в уже открытом PR #14 (`antigravity/repo-hygiene`). Оба находятся в release-workflow, которые **не запускаются на pull request** — они триггерятся только по тегу и `workflow_dispatch`, поэтому зелёный CI на PR их не покрывает.
|
||
|
||
### 1.1. `permissions` стоит на уровне шага — это недопустимый ключ
|
||
|
||
Три места: `linux-release.yml:63`, `linux-release.yml:127`, `windows-release.yml:86`.
|
||
|
||
```yaml
|
||
- name: Attach bootstrap to GitHub Release
|
||
uses: softprops/action-gh-release@3bb1273…
|
||
permissions: # ← уровень шага
|
||
contents: write
|
||
with:
|
||
```
|
||
|
||
В схеме GitHub Actions `permissions` существует только на уровне workflow и на уровне job. У шага такого ключа нет. При срабатывании по тегу workflow либо не загрузится с ошибкой `Unexpected value 'permissions'`, либо шаг выполнится с унаследованным `contents: read` и публикация релиза упадёт с 403.
|
||
|
||
Исправление: перенести `permissions: contents: write` на уровень job. В `linux-release.yml` это оба job — `bootstrap` и `publish`; в `windows-release.yml` — `package`. Workflow-уровень оставить `contents: read`.
|
||
|
||
Job-уровень — самая мелкая гранулярность, которую даёт GitHub; более узко не бывает.
|
||
|
||
### 1.2. SBOM генерируется и выбрасывается
|
||
|
||
Три места: `linux-release.yml:57`, `linux-release.yml:111`, `windows-release.yml:67`.
|
||
|
||
Файл SBOM не попадает ни в `path:` шага `upload-artifact`, ни в `files:` шага `action-gh-release`. То есть он создаётся и остаётся в рабочем каталоге runner'а. Требование задания «приложить к артефактам релиза» не выполнено.
|
||
|
||
Сопутствующее:
|
||
|
||
- `|| true` гасит любую ошибку генерации: сломанный шаг выглядит зелёным, а SBOM молча отсутствует. Для артефакта цепочки поставки это обесценивает саму затею — убрать;
|
||
- в job `bootstrap` файла `linux-release.yml` нет шага `setup-dotnet`, а SBOM пишется в подкаталог `sbom/`, тогда как остальные пути в этом job — корневые;
|
||
- SBOM в job `bootstrap` и в job `publish` строится из одного и того же `ServerMonitorManager.slnx`, то есть дублируется.
|
||
|
||
Исправление: генерировать SBOM один раз, до шага загрузки, в job, где уже настроен .NET; добавить файл и в `upload-artifact`, и в `files:` релиза; `|| true` убрать. Дублирующую генерацию в job `bootstrap` удалить.
|
||
|
||
### 1.3. Как проверить себя
|
||
|
||
Release-workflow нельзя прогнать через PR. Проверять так:
|
||
|
||
- `actionlint` на все пять файлов `.github/workflows/` — он ловит и недопустимые ключи, и опечатки в схеме;
|
||
- либо `gh workflow run` вручную по `workflow_dispatch` на своей ветке, если это не приведёт к публикации релиза (шаги релиза защищены `if: startsWith(github.ref, 'refs/tags/')`, поэтому на ветке они пропускаются, а разбор файла всё равно произойдёт).
|
||
|
||
В отчёте показать вывод проверки. Утверждение «исправлено» без демонстрации разбора файла не принимается — именно отсутствие такой проверки и привело к дефекту.
|
||
|
||
---
|
||
|
||
## Часть 2 — новая задача
|
||
|
||
### Репозиторий и ветка
|
||
|
||
- Репозиторий: https://github.com/ochenstarik-ui/server-monitor-manager
|
||
- База: `main` (после merge PR #13 и #14)
|
||
- Ветка: `antigravity/reproducible-builds`
|
||
- Рабочая папка: `C:\Users\Ochenstarik\projects\smm-antigravity`
|
||
|
||
### Зачем
|
||
|
||
`docs/installer-contract.md` §1 требует, чтобы production-установка использовала закреплённый релиз, а `docs/product-horizons.md`, Горизонт 0, требует подписанной поставки с хэшами всех артефактов. Обе гарантии опираются на предположение, что из одного коммита получается один и тот же бинарник.
|
||
|
||
Сейчас это неверно: версии NuGet-пакетов заданы в каждом `.csproj` по отдельности, lock-файлов нет, восстановление зависимостей идёт по диапазонам. Сборка одного и того же тега завтра может дать другой набор зависимостей, и никакая подпись артефакта этого не покажет.
|
||
|
||
Задача закрывает предпосылку, без которой подписанная поставка не имеет смысла.
|
||
|
||
### Работы
|
||
|
||
**1. `Directory.Packages.props` в корне.**
|
||
|
||
Централизованное управление версиями: `ManagePackageVersionsCentrally` включено, все `PackageVersion` собраны в одном файле, из `.csproj` версии убраны, остаются только `PackageReference` без атрибута `Version`.
|
||
|
||
Затрагиваются все проекты решения: `Core`, `Control`, `Agent`, `Desktop`, `Provisioning.Helper`, тестовый проект.
|
||
|
||
**2. Lock-файлы.**
|
||
|
||
`RestorePackagesWithLockFile` включён, `packages.lock.json` каждого проекта закоммичен. В CI восстановление выполняется с `--locked-mode`, чтобы расхождение lock-файла с проектом ломало сборку, а не молча подтягивало другую версию.
|
||
|
||
**3. `Directory.Build.props` в корне.**
|
||
|
||
- `Nullable`, `EnableNETAnalyzers`, `AnalysisLevel latest-recommended`;
|
||
- `InvariantGlobalization` там, где это уместно для серверных проектов;
|
||
- детерминированная сборка: `Deterministic`, `ContinuousIntegrationBuild` под условием `$(CI)`;
|
||
- `TreatWarningsAsErrors` **не включать** — см. границы.
|
||
|
||
**4. CI.**
|
||
|
||
В `linux-control-agent.yml` и `windows-build.yml` добавить восстановление в locked-режиме. Прогон должен падать, если lock-файл не соответствует проектам.
|
||
|
||
### Границы
|
||
|
||
Не изменять:
|
||
|
||
- `src/ServerMonitorManager.Control/LinkService.cs`, `LinkPolicyApplier.cs`, `LinkReconciliationBackgroundService.cs`, `ControlStore.cs`, `ControlMaintenance.cs`, `ControlOptions.cs`, `appsettings.json`;
|
||
- `src/ServerMonitorManager.Provisioning.Helper/**`, `deploy/**`;
|
||
- `CertificateAuthority.cs`, `CertificateLifecycleService.cs`, `src/ServerMonitorManager.Agent/AgentClient.cs`;
|
||
- содержимое файлов в `tests/**` — правки допускаются только в `.csproj` тестового проекта.
|
||
|
||
Первые две группы заняты заданием B-3R, третья — заданием по жизненному циклу сертификатов.
|
||
|
||
Не входит в задание:
|
||
|
||
- **`TreatWarningsAsErrors`.** Включение сейчас сломает незавершённую ветку `hermes/task3-b3-fact-reconciliation` при её ребейзе на main. Отдельная задача после её merge;
|
||
- разделение тестов на `Core.Tests` / `Control.Tests` / `Agent.Tests` / `Helper.Tests` — тестовые файлы заняты;
|
||
- обновление версий пакетов: задача переносит существующие версии как есть, а не апгрейдит их. Любое изменение номера версии должно быть отдельно обосновано в отчёте;
|
||
- merge собственного PR.
|
||
|
||
### Критерий приёмки
|
||
|
||
- ни один `.csproj` не содержит `Version=` в `PackageReference`;
|
||
- `packages.lock.json` закоммичены для всех проектов;
|
||
- CI восстанавливает зависимости в `--locked-mode` и падает при рассинхроне — продемонстрировать намеренной поломкой lock-файла в отдельном временном коммите, показать красный прогон, затем откатить;
|
||
- номера версий пакетов не изменились относительно `main` — показать сравнение;
|
||
- `git diff --name-only main` не содержит ни одного файла из списка границ;
|
||
- Control suite прогнан на Linux;
|
||
- Desktop собирается — в CI, локально WinUI 3 собрать нельзя, если нет Windows App SDK;
|
||
- CI зелёный, PR создан и не смержен.
|
||
|
||
### Отчётность
|
||
|
||
Раздельно: что проверено локально, что в CI со ссылками на прогоны, что не проверялось и почему.
|
||
|
||
Отдельно показать: вывод `actionlint` по части 1 и сравнение версий пакетов до и после по части 2.
|
||
|
||
Физический acceptance (`SMM_ACCEPT_RESTORE=1 SMM_ACCEPT_REBOOT=1 tests/acceptance/three-server-mesh.sh`) выполнить нельзя — не выданы SSH- и topology-параметры.
|