diff --git a/docs/RELEASE_CANDIDATE_RUNBOOK_RU.md b/docs/RELEASE_CANDIDATE_RUNBOOK_RU.md new file mode 100644 index 0000000..b562cb5 --- /dev/null +++ b/docs/RELEASE_CANDIDATE_RUNBOOK_RU.md @@ -0,0 +1,80 @@ +# Release Candidate Runbook + +Этот документ описывает техническую сборку Release Candidate для AWatch-rus. RC-сборка нужна, чтобы одной воспроизводимой командой собрать проверенные артефакты, зафиксировать git commit, сформировать SBOM/manifest/checksums и сложить результат в отдельный каталог под конкретное имя кандидата. + +Release Candidate не равен юридической готовности к подаче в реестр и не заменяет финальную процедуру релиза. + +## Запуск + +Команда выполняется из корня репозитория: + +```bash +bash scripts/build_release_candidate.sh v1.0.2-rc1 +``` + +Первый аргумент обязателен. Имя кандидата используется как имя каталога в `dist/release-candidate/`, поэтому скрипт требует начало с буквы или цифры и дальше принимает только буквы, цифры, точку, подчеркивание и дефис. + +Перед сборкой рабочее дерево git должно быть чистым. Если есть незакоммиченные, staged или untracked файлы, скрипт завершится с ошибкой. Это защищает RC от незафиксированного состояния. + +## Проверки + +Скрипт выполняет обязательные проверки и сборку Rust workspace: + +```bash +cargo fmt --manifest-path adk-rust/Cargo.toml --all -- --check +cargo test --manifest-path adk-rust/Cargo.toml --workspace +cargo clippy --manifest-path adk-rust/Cargo.toml --workspace --all-targets -- -D warnings +cargo build --manifest-path adk-rust/Cargo.toml --workspace --release +bash scripts/quality-gate.sh +bash scripts/check_private_config_guard.sh +node scripts/check_portal_contract_sync.mjs +``` + +Если любая проверка падает, RC-сборка считается несостоявшейся. +Неполный output-каталог при ошибке удаляется, чтобы не смешивать частичные артефакты с валидной сборкой. + +## Артефакты + +Результат складывается в: + +```text +dist/release-candidate// +``` + +Для примера выше итоговый каталог будет: + +```text +dist/release-candidate/v1.0.2-rc1/ +``` + +В каталоге создаются: + +- `git-commit.txt` - commit, из которого собран кандидат; +- `FILES.txt` - список файлов, покрытых итоговыми checksum, кроме самого `SHA256SUMS.txt`; +- `SHA256SUMS.txt` - SHA-256 для всех файлов каталога, кроме самого `SHA256SUMS.txt`; +- `sbom/` - SBOM-файлы, созданные существующим генератором `scripts/generate_release_sbom_v0_2.sh`; +- `RELEASE_ASSETS_MANIFEST-v0.2.json` и `SHA256SUMS-v0.2.txt` - manifest/checksums, которые формирует существующий SBOM generator. + +Каталог `dist/` не предназначен для коммита в git. + +## Проверка checksum + +Для проверки итоговых checksum: + +```bash +cd dist/release-candidate/v1.0.2-rc1 +sha256sum -c SHA256SUMS.txt +``` + +Ожидаемый результат - `OK` для всех записей. Любая ошибка означает, что набор артефактов изменился после сборки или поврежден. + +## Перед реальной подачей + +Release Candidate подтверждает техническую воспроизводимость сборки, но перед реальной подачей все еще нужны: + +- release tag; +- release-specific SBOM; +- license review; +- signed/checksummed artifacts; +- проверка отсутствия live/private data; +- финальные install/user/admin guide под конкретную версию. diff --git a/scripts/build_release_candidate.sh b/scripts/build_release_candidate.sh new file mode 100644 index 0000000..027e5c6 --- /dev/null +++ b/scripts/build_release_candidate.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$ROOT_DIR" + +RC_NAME="${1:-}" +if [[ -z "$RC_NAME" ]]; then + cat >&2 <<'EOF' +usage: bash scripts/build_release_candidate.sh +example: bash scripts/build_release_candidate.sh v1.0.2-rc1 +EOF + exit 2 +fi + +if [[ ! "$RC_NAME" =~ ^[A-Za-z0-9][A-Za-z0-9._-]*$ ]]; then + echo "invalid release candidate name: start with a letter or number; use only letters, numbers, dot, underscore, and hyphen" >&2 + exit 2 +fi + +if [[ -n "$(git status --porcelain --untracked-files=normal)" ]]; then + echo "git working tree is not clean; commit, stash, or remove changes before building release candidate" >&2 + git status --short >&2 + exit 1 +fi + +OUT_DIR="$ROOT_DIR/dist/release-candidate/$RC_NAME" +if [[ -e "$OUT_DIR" ]]; then + echo "release candidate output already exists: $OUT_DIR" >&2 + exit 1 +fi + +BUILD_SUCCESS=0 +cleanup_on_failure() { + status=$? + if [[ $status -ne 0 && $BUILD_SUCCESS -ne 1 && -d "$OUT_DIR" ]]; then + rm -rf "$OUT_DIR" + fi + exit "$status" +} +trap cleanup_on_failure EXIT + +mkdir -p "$OUT_DIR" + +git rev-parse HEAD > "$OUT_DIR/git-commit.txt" + +cargo fmt --manifest-path adk-rust/Cargo.toml --all -- --check +cargo test --manifest-path adk-rust/Cargo.toml --workspace +cargo clippy --manifest-path adk-rust/Cargo.toml --workspace --all-targets -- -D warnings +cargo build --manifest-path adk-rust/Cargo.toml --workspace --release +bash scripts/quality-gate.sh +bash scripts/check_private_config_guard.sh +node scripts/check_portal_contract_sync.mjs + +bash scripts/generate_release_sbom_v0_2.sh "$OUT_DIR" + +( + cd "$OUT_DIR" + { + printf '%s\n' "FILES.txt" + find . -type f ! -name 'FILES.txt' ! -name 'SHA256SUMS.txt' -print \ + | sort \ + | sed 's#^\./##' + } > FILES.txt + + find . -type f ! -name 'SHA256SUMS.txt' -print0 \ + | sort -z \ + | xargs -0 sha256sum > SHA256SUMS.txt +) + +BUILD_SUCCESS=1 +echo "release candidate built: $OUT_DIR"