docs(registry): add Russian build runner and release evidence plan

This commit is contained in:
igor04091968
2026-06-21 07:10:43 +03:00
parent 6861df9be4
commit f5e9c81c5b
12 changed files with 946 additions and 19 deletions
+312
View File
@@ -0,0 +1,312 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT"
export GIT_LFS_SKIP_SMUDGE=1
RELEASE_VERSION="${RELEASE_VERSION:-registry-candidate-$(date +%Y%m%d-%H%M%S)}"
RELEASE_COMMIT="${RELEASE_COMMIT:-$(git rev-parse HEAD)}"
OUTPUT_DIR="${OUTPUT_DIR:-release-evidence/${RELEASE_VERSION}}"
DOCS_ONLY="${DOCS_ONLY:-0}"
CARGO_MANIFEST_PATH="${CARGO_MANIFEST_PATH:-}"
if [[ -z "$CARGO_MANIFEST_PATH" ]]; then
if [[ -f Cargo.toml ]]; then
CARGO_MANIFEST_PATH="Cargo.toml"
elif [[ -f adk-rust/Cargo.toml ]]; then
CARGO_MANIFEST_PATH="adk-rust/Cargo.toml"
fi
fi
if [[ -n "$CARGO_MANIFEST_PATH" ]]; then
CARGO_WORKSPACE_DIR="$(cd "$(dirname "$CARGO_MANIFEST_PATH")" && pwd)"
else
CARGO_WORKSPACE_DIR=""
fi
LOG_DIR="$OUTPUT_DIR/logs"
ARTIFACT_DIR="$OUTPUT_DIR/artifacts"
SKIP_FILE="$OUTPUT_DIR/skipped-checks.txt"
CHECKS_FILE="$OUTPUT_DIR/checks.tsv"
mkdir -p "$LOG_DIR" "$ARTIFACT_DIR"
: > "$SKIP_FILE"
: > "$CHECKS_FILE"
record_check() {
local name="$1"
local status="$2"
local detail="${3:-}"
printf '%s\t%s\t%s\n' "$name" "$status" "$detail" >> "$CHECKS_FILE"
}
run_logged() {
local name="$1"
shift
local log="$LOG_DIR/${name}.log"
{
printf 'command:'
printf ' %q' "$@"
printf '\nstarted_at: %s\n\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
} > "$log"
if "$@" >> "$log" 2>&1; then
printf '\nfinished_at: %s\nstatus: ok\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$log"
record_check "$name" "ok" "$log"
else
local status=$?
printf '\nfinished_at: %s\nstatus: fail\nexit_code: %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$status" >> "$log"
record_check "$name" "fail" "$log"
return "$status"
fi
}
run_logged_in_dir() {
local name="$1"
local dir="$2"
shift 2
local log="$LOG_DIR/${name}.log"
{
printf 'working_dir: %s\n' "$dir"
printf 'command:'
printf ' %q' "$@"
printf '\nstarted_at: %s\n\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
} > "$log"
if (cd "$dir" && "$@") >> "$log" 2>&1; then
printf '\nfinished_at: %s\nstatus: ok\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$log"
record_check "$name" "ok" "$log"
else
local status=$?
printf '\nfinished_at: %s\nstatus: fail\nexit_code: %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$status" >> "$log"
record_check "$name" "fail" "$log"
return "$status"
fi
}
run_optional() {
local name="$1"
shift
if "$@"; then
record_check "$name" "ok" ""
else
local status=$?
record_check "$name" "skipped" "exit_code=$status"
printf '%s: skipped: exit_code=%s\n' "$name" "$status" >> "$SKIP_FILE"
fi
}
skip_check() {
local name="$1"
local reason="$2"
record_check "$name" "skipped" "$reason"
printf '%s: skipped: %s\n' "$name" "$reason" >> "$SKIP_FILE"
}
capture_command() {
local name="$1"
shift
local log="$LOG_DIR/${name}.log"
if command -v "$1" >/dev/null 2>&1; then
"$@" > "$log" 2>&1 || true
else
printf 'skipped: tool not installed: %s\n' "$1" > "$log"
fi
}
if [[ "$RELEASE_COMMIT" != "$(git rev-parse HEAD)" ]]; then
git cat-file -e "${RELEASE_COMMIT}^{commit}"
git -c filter.lfs.smudge= -c filter.lfs.process= -c filter.lfs.required=false checkout --detach "$RELEASE_COMMIT"
fi
{
git remote -v
} > "$LOG_DIR/git-remotes.log" 2>&1
git status --short > "$LOG_DIR/git-status-short.log" 2>&1
git rev-parse HEAD > "$LOG_DIR/git-rev-parse-head.log" 2>&1
git log --oneline -20 > "$LOG_DIR/git-log-oneline-20.log" 2>&1
capture_command rustc-version rustc --version
capture_command cargo-version cargo --version
uname -a > "$LOG_DIR/uname.log" 2>&1
capture_command hostnamectl hostnamectl
df -h > "$LOG_DIR/df-h.log" 2>&1
free -h > "$LOG_DIR/free-h.log" 2>&1 || true
if [[ "$DOCS_ONLY" == "1" ]]; then
skip_check "cargo_fmt" "DOCS_ONLY=1"
skip_check "cargo_test_workspace" "DOCS_ONLY=1"
skip_check "cargo_clippy_workspace" "DOCS_ONLY=1"
skip_check "cargo_build_workspace_release" "DOCS_ONLY=1"
elif [[ -z "$CARGO_WORKSPACE_DIR" ]]; then
run_logged cargo-workspace-missing false
else
run_logged_in_dir cargo-fmt "$CARGO_WORKSPACE_DIR" cargo fmt --all --check
run_logged_in_dir cargo-test-workspace "$CARGO_WORKSPACE_DIR" cargo test --workspace
run_logged_in_dir cargo-clippy-workspace "$CARGO_WORKSPACE_DIR" cargo clippy --workspace --all-targets -- -D warnings
run_logged_in_dir cargo-build-workspace-release "$CARGO_WORKSPACE_DIR" cargo build --workspace --release
fi
if [[ -f scripts/registry_readiness_check.sh ]]; then
run_logged registry-readiness-check bash scripts/registry_readiness_check.sh
else
skip_check "registry_readiness_check" "script missing"
fi
for smoke in \
scripts/deployment-readiness-smoke.mjs \
scripts/pilot-validation-smoke.mjs \
scripts/browser-conformance-smoke.mjs
do
if [[ -f "$smoke" ]]; then
if [[ "$smoke" == *browser-conformance-smoke.mjs ]]; then
skip_check "$(basename "$smoke")" "requires live stand unless explicitly run by operator"
continue
fi
run_logged "$(basename "$smoke" .mjs)" node "$smoke"
else
skip_check "$(basename "$smoke")" "script missing"
fi
done
if [[ -f scripts/validate_install_kit.sh ]]; then
run_logged validate-install-kit bash scripts/validate_install_kit.sh
else
skip_check "validate_install_kit" "script missing"
fi
if [[ "$DOCS_ONLY" == "1" ]]; then
skip_check "cargo_metadata" "DOCS_ONLY=1"
skip_check "cargo_tree" "DOCS_ONLY=1"
elif [[ -n "$CARGO_WORKSPACE_DIR" ]] && command -v cargo >/dev/null 2>&1; then
run_logged_in_dir cargo-metadata "$CARGO_WORKSPACE_DIR" cargo metadata --format-version 1 --no-deps
cp "$LOG_DIR/cargo-metadata.log" "$OUTPUT_DIR/cargo-metadata.json"
if (cd "$CARGO_WORKSPACE_DIR" && cargo tree --version >/dev/null 2>&1); then
run_logged_in_dir cargo-tree "$CARGO_WORKSPACE_DIR" cargo tree
cp "$LOG_DIR/cargo-tree.log" "$OUTPUT_DIR/cargo-tree.txt"
else
skip_check "cargo_tree" "cargo tree not available"
fi
elif [[ -z "$CARGO_WORKSPACE_DIR" ]]; then
skip_check "cargo_metadata" "Cargo.toml not found"
skip_check "cargo_tree" "Cargo.toml not found"
else
skip_check "cargo_metadata" "cargo not installed"
skip_check "cargo_tree" "cargo not installed"
fi
SOURCE_ARCHIVE="$ARTIFACT_DIR/${RELEASE_VERSION}-source.tar.gz"
git -c filter.lfs.smudge= -c filter.lfs.process= -c filter.lfs.required=false archive --format=tar.gz --output="$SOURCE_ARCHIVE" HEAD
record_check "source_archive" "ok" "$SOURCE_ARCHIVE"
BINARY_ARCHIVE="$ARTIFACT_DIR/${RELEASE_VERSION}-binaries.tar.gz"
if [[ "$DOCS_ONLY" == "1" ]]; then
printf 'skipped: DOCS_ONLY=1\n' > "$BINARY_ARCHIVE.skip"
skip_check "binary_archive" "DOCS_ONLY=1"
elif [[ -d target/release ]]; then
tar -czf "$BINARY_ARCHIVE" target/release
record_check "binary_archive" "ok" "$BINARY_ARCHIVE"
else
skip_check "binary_archive" "target/release missing"
fi
if command -v cargo-cyclonedx >/dev/null 2>&1; then
run_logged sbom-cyclonedx cargo cyclonedx --format json --output-file "$OUTPUT_DIR/sbom-cyclonedx.json"
else
skip_check "sbom_cyclonedx" "cargo-cyclonedx not installed"
fi
if command -v syft >/dev/null 2>&1; then
run_logged sbom-syft-cyclonedx syft . -o cyclonedx-json="$OUTPUT_DIR/sbom-syft-cyclonedx.json"
run_logged sbom-syft-spdx syft . -o spdx-json="$OUTPUT_DIR/sbom-spdx.json"
else
skip_check "sbom_spdx" "syft not installed"
skip_check "sbom_syft_cyclonedx" "syft not installed"
fi
GENERATED_AT="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
BUILD_RUNNER_HOST="$(hostname 2>/dev/null || printf unknown)"
PRIMARY_SOURCE_REPOSITORY="https://git.iri1968.dpdns.org/awatch-rus/AWatch-rus"
python3 - "$OUTPUT_DIR/release-evidence-manifest.json" "$RELEASE_VERSION" "$RELEASE_COMMIT" "$GENERATED_AT" "$BUILD_RUNNER_HOST" "$PRIMARY_SOURCE_REPOSITORY" <<'PY'
import json
import sys
from pathlib import Path
manifest_path, release_version, release_commit, generated_at, build_runner, primary_source = sys.argv[1:]
root = Path(manifest_path).parent
checks = []
checks_file = root / "checks.tsv"
if checks_file.exists():
for line in checks_file.read_text(encoding="utf-8").splitlines():
name, status, detail = (line.split("\t") + ["", ""])[:3]
checks.append({"name": name, "status": status, "detail": detail})
artifacts = []
for path in sorted((root / "artifacts").glob("*")):
if path.is_file():
artifacts.append(path.relative_to(root).as_posix())
data = {
"product": "AWatch-rus",
"release_version": release_version,
"release_commit": release_commit,
"build_runner": build_runner,
"primary_source_repository": primary_source,
"github_role": "public_mirror_only",
"generated_at": generated_at,
"checks": checks,
"artifacts": artifacts,
"docs_only": bool(__import__("os").environ.get("DOCS_ONLY") == "1"),
}
Path(manifest_path).write_text(json.dumps(data, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
PY
cat > "$OUTPUT_DIR/RELEASE_EVIDENCE_REPORT_RU.md" <<EOF
# Release evidence report
Product: AWatch-rus
Release version: ${RELEASE_VERSION}
Release commit: ${RELEASE_COMMIT}
Generated at: ${GENERATED_AT}
Build runner: ${BUILD_RUNNER_HOST}
Primary source repository: ${PRIMARY_SOURCE_REPOSITORY}
GitHub role: public mirror only.
Responsible person: [ЗАПОЛНИТЬ ПРАВООБЛАДАТЕЛЕМ]
DOCS_ONLY: ${DOCS_ONLY}
## Checks
See \`checks.tsv\` and \`logs/\`.
## Skipped checks
See \`skipped-checks.txt\`.
## Conservative scope
This report does not claim FSTEC/FSB certification. It does not claim SIEM
replacement. It does not claim DLP replacement. It does not claim ML/LLM-based
detection. It does not claim cloud dependency. It does not claim automatic
remediation. It does not claim completed legal registration in the Russian
software registry.
EOF
(
cd "$OUTPUT_DIR"
find . -type f ! -name SHA256SUMS -print0 | sort -z | xargs -0 sha256sum > SHA256SUMS
)
if [[ -f scripts/check_release_evidence.sh ]]; then
bash scripts/check_release_evidence.sh "$OUTPUT_DIR"
else
skip_check "release_evidence_check" "script missing"
fi
printf 'release_evidence_dir=%s\n' "$OUTPUT_DIR"
+129
View File
@@ -0,0 +1,129 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ $# -ne 1 ]]; then
printf 'usage: %s <release-evidence-dir>\n' "$0" >&2
exit 2
fi
EVIDENCE_DIR="$1"
failures=()
fail() {
failures+=("$1")
}
require_file() {
local path="$1"
if [[ ! -s "$EVIDENCE_DIR/$path" ]]; then
fail "missing_or_empty:$path"
fi
}
require_dir() {
local path="$1"
if [[ ! -d "$EVIDENCE_DIR/$path" ]]; then
fail "missing_directory:$path"
fi
}
if [[ ! -d "$EVIDENCE_DIR" ]]; then
printf 'release_evidence_check=fail\nmissing_directory:%s\n' "$EVIDENCE_DIR"
exit 2
fi
require_file "release-evidence-manifest.json"
require_file "RELEASE_EVIDENCE_REPORT_RU.md"
require_file "SHA256SUMS"
require_dir "logs"
require_dir "artifacts"
if [[ -s "$EVIDENCE_DIR/SHA256SUMS" ]]; then
(cd "$EVIDENCE_DIR" && sha256sum -c SHA256SUMS >/dev/null) || fail "sha256sum_check_failed"
fi
if [[ -s "$EVIDENCE_DIR/release-evidence-manifest.json" ]]; then
if command -v jq >/dev/null 2>&1; then
jq -e . "$EVIDENCE_DIR/release-evidence-manifest.json" >/dev/null || fail "invalid_json:release-evidence-manifest.json"
jq -e '
.product == "AWatch-rus"
and (.release_version | type == "string" and length > 0)
and (.release_commit | type == "string" and length > 0)
and (.build_runner | type == "string" and length > 0)
and .primary_source_repository == "https://git.iri1968.dpdns.org/awatch-rus/AWatch-rus"
and .github_role == "public_mirror_only"
and (.generated_at | type == "string" and length > 0)
and (.checks | type == "array")
and (.artifacts | type == "array")
' "$EVIDENCE_DIR/release-evidence-manifest.json" >/dev/null || fail "manifest_required_fields"
elif command -v python3 >/dev/null 2>&1; then
python3 - "$EVIDENCE_DIR/release-evidence-manifest.json" <<'PY' || fail "manifest_required_fields"
import json
import sys
with open(sys.argv[1], "r", encoding="utf-8") as fh:
data = json.load(fh)
required = {
"product": "AWatch-rus",
"primary_source_repository": "https://git.iri1968.dpdns.org/awatch-rus/AWatch-rus",
"github_role": "public_mirror_only",
}
for key, value in required.items():
if data.get(key) != value:
raise SystemExit(f"{key} mismatch")
for key in ("release_version", "release_commit", "build_runner", "generated_at"):
if not isinstance(data.get(key), str) or not data[key]:
raise SystemExit(f"{key} missing")
if not isinstance(data.get("checks"), list):
raise SystemExit("checks missing")
if not isinstance(data.get("artifacts"), list):
raise SystemExit("artifacts missing")
PY
else
fail "json_validator_missing:jq_or_python3_required"
fi
fi
if [[ ! -s "$EVIDENCE_DIR/cargo-metadata.json" ]] && ! grep -Eiq 'cargo_metadata.*skipped' "$EVIDENCE_DIR/skipped-checks.txt" 2>/dev/null; then
fail "missing_cargo_metadata_or_documented_skip"
fi
if [[ ! -s "$EVIDENCE_DIR/cargo-tree.txt" ]] && ! grep -Eiq 'cargo_tree.*skipped' "$EVIDENCE_DIR/skipped-checks.txt" 2>/dev/null; then
fail "missing_cargo_tree_or_documented_skip"
fi
if ! find "$EVIDENCE_DIR/artifacts" -maxdepth 1 -type f -name '*source*.tar.gz' | grep -q .; then
fail "missing_source_archive"
fi
if ! find "$EVIDENCE_DIR/artifacts" -maxdepth 1 -type f \( -name '*binaries*.tar.gz' -o -name '*binaries*.tar.gz.skip' \) | grep -q .; then
fail "missing_binary_archive_or_documented_skip"
fi
scan_files=(
"$EVIDENCE_DIR/RELEASE_EVIDENCE_REPORT_RU.md"
"$EVIDENCE_DIR/release-evidence-manifest.json"
)
if grep -RInEi "(ФСТЭК|ФСБ).{0,80}(сертифицирован|сертификация|сертификат).{0,80}(есть|получен|имеется|подтвержден)|сертифицированное[[:space:]]+СЗИ" "${scan_files[@]}" >/tmp/release_evidence_forbidden_cert.$$ 2>/dev/null; then
fail "forbidden_claim_certification:$(cat /tmp/release_evidence_forbidden_cert.$$)"
fi
rm -f /tmp/release_evidence_forbidden_cert.$$
if grep -RInEi "(replaces|заменяет).{0,80}(SIEM|DLP)|(SIEM|DLP).{0,80}(replacement|заменяет)" "${scan_files[@]}" \
| grep -Eiv "(does not|not |not_claimed|не |forbidden|не заявляет)" \
>/tmp/release_evidence_forbidden_replace.$$ 2>/dev/null; then
fail "forbidden_claim_siem_dlp_replacement:$(cat /tmp/release_evidence_forbidden_replace.$$)"
fi
rm -f /tmp/release_evidence_forbidden_replace.$$
if ((${#failures[@]} > 0)); then
printf 'release_evidence_check=fail\n'
for failure in "${failures[@]}"; do
printf '%s\n' "$failure"
done
exit 2
fi
printf 'release_evidence_check=ok\n'
+58 -11
View File
@@ -37,11 +37,17 @@ required_files=(
"docs/registry/GIT_RU_MIRRORING_RUNBOOK_RU.md"
"docs/registry/GITEA_BACKUP_AND_RESTORE_RUNBOOK_RU.md"
"docs/registry/WIKI_AND_DOCUMENTATION_POLICY_RU.md"
"docs/registry/RU_BUILD_RUNNER_READINESS_RU.md"
"docs/registry/BUILD_RUNNER_SETUP_RUNBOOK_RU.md"
"docs/registry/RELEASE_EVIDENCE_RUNBOOK_RU.md"
"docs/registry/RELEASE_ARTIFACTS_STORAGE_RU.md"
"docs/registry/RELEASE_EVIDENCE_MANIFEST_RU.md"
"docs/registry/INSTALLATION_AND_TEST_INSTANCE_RU.md"
"docs/registry/LIFECYCLE_AND_SUPPORT_RU.md"
"docs/registry/REGISTRY_READINESS_CHANGELOG_RU.md"
"docs/registry/registry-evidence-manifest.json"
"scripts/build_release_evidence.sh"
"scripts/check_release_evidence.sh"
"README.md"
)
@@ -68,6 +74,11 @@ if [[ -s "$MANIFEST" ]]; then
and .backup.retention_days == 14
and .backup.systemd_timer == "awatch-gitea-backup.timer"
and .backup.restore_tested == false
and .build_runner.status != "production_ready"
and .build_runner.status == "planned"
and .build_runner.target_hostname == "awatch-build-01"
and .build_runner.separate_from_git_server == true
and (.build_runner.required_checks | index("release_evidence_check") != null)
' "$MANIFEST" >/dev/null || fail "manifest_required_fields"
elif command -v python3 >/dev/null 2>&1; then
python3 -m json.tool "$MANIFEST" >/dev/null || fail "invalid_json:docs/registry/registry-evidence-manifest.json"
@@ -75,8 +86,7 @@ if [[ -s "$MANIFEST" ]]; then
import json
import sys
path = sys.argv[1]
with open(path, "r", encoding="utf-8") as fh:
with open(sys.argv[1], "r", encoding="utf-8") as fh:
data = json.load(fh)
expected = {
@@ -113,19 +123,32 @@ backup_expected = {
for key, value in backup_expected.items():
if backup.get(key) != value:
raise SystemExit(f"backup.{key} mismatch")
build_runner = data.get("build_runner") or {}
if build_runner.get("status") == "production_ready":
raise SystemExit("build_runner.status must not be production_ready")
if build_runner.get("status") != "planned":
raise SystemExit("build_runner.status mismatch")
if build_runner.get("target_hostname") != "awatch-build-01":
raise SystemExit("build_runner.target_hostname mismatch")
if build_runner.get("separate_from_git_server") is not True:
raise SystemExit("build_runner.separate_from_git_server mismatch")
if "release_evidence_check" not in build_runner.get("required_checks", []):
raise SystemExit("build_runner.required_checks missing release_evidence_check")
PY
elif command -v node >/dev/null 2>&1; then
node -e 'JSON.parse(require("fs").readFileSync(process.argv[1], "utf8"))' "$MANIFEST" \
|| fail "invalid_json:docs/registry/registry-evidence-manifest.json"
else
fail "json_validator_missing:python3_or_node_required"
fail "json_validator_missing:jq_or_python3_required"
fi
fi
require_grep "git\\.iri1968\\.dpdns\\.org" "docs/registry/SOURCE_CODE_AND_BUILD_INFRASTRUCTURE_RU.md" "gitea_domain_source_infra"
require_grep "git\\.iri1968\\.dpdns\\.org" "docs/registry/GIT_RU_MIRRORING_RUNBOOK_RU.md" "gitea_domain_git_runbook"
require_grep "git\\.iri1968\\.dpdns\\.org" "docs/registry/WIKI_AND_DOCUMENTATION_POLICY_RU.md" "gitea_domain_wiki_policy"
require_grep "git\\.iri1968\\.dpdns\\.org" "docs/registry/RU_BUILD_RUNNER_READINESS_RU.md" "gitea_domain_build_runner"
require_grep "git\\.iri1968\\.dpdns\\.org" "README.md" "gitea_domain_readme"
require_grep "awatch-build-01" "docs/registry/RU_BUILD_RUNNER_READINESS_RU.md" "awatch_build_runner_readiness"
require_grep "awatch-build-01" "docs/registry/BUILD_RUNNER_SETUP_RUNBOOK_RU.md" "awatch_build_runner_setup"
require_grep "awatch-build-01" "docs/registry/RELEASE_EVIDENCE_RUNBOOK_RU.md" "awatch_build_runner_release_runbook"
require_grep "GitHub[[:space:]]*=[[:space:]]*public mirror only|public mirror only" "docs/registry/SOURCE_CODE_AND_BUILD_INFRASTRUCTURE_RU.md" "github_public_mirror_source_infra"
require_grep "GitHub[[:space:]]*=[[:space:]]*public mirror only|public mirror only" "docs/registry/GIT_RU_MIRRORING_RUNBOOK_RU.md" "github_public_mirror_git_runbook"
require_grep "GitHub.*публичн|public mirror" "docs/registry/WIKI_AND_DOCUMENTATION_POLICY_RU.md" "github_public_mirror_wiki_policy"
@@ -136,31 +159,55 @@ require_grep "sha256|SHA256" "docs/registry/GITEA_BACKUP_AND_RESTORE_RUNBOOK_RU.
require_grep "restore_tested=false|restore_tested..false|\"restore_tested\"[[:space:]]*:[[:space:]]*false" "docs/registry/GITEA_BACKUP_AND_RESTORE_RUNBOOK_RU.md" "restore_tested_false_runbook"
require_grep "\"restore_tested\"[[:space:]]*:[[:space:]]*false" "docs/registry/registry-evidence-manifest.json" "restore_tested_false_manifest"
require_grep "docs/registry" "docs/registry/WIKI_AND_DOCUMENTATION_POLICY_RU.md" "authoritative_docs_path_wiki_policy"
require_grep "release_evidence_check" "docs/registry/registry-evidence-manifest.json" "release_evidence_check_manifest"
scan_files=(
"$ROOT/README.md"
"$REGISTRY_DIR"/*.md
"$REGISTRY_DIR"/*.json
"$ROOT/scripts/build_release_evidence.sh"
"$ROOT/scripts/check_release_evidence.sh"
)
if grep -RInEi "(ФСТЭК|ФСБ).{0,80}(сертифицирован|сертификация|сертификат).{0,80}(есть|получен|имеется|подтвержден)" "${scan_files[@]}" >/tmp/registry_forbidden_fstec_fsb.$$ 2>/dev/null; then
claim_scan_files=(
"$ROOT/README.md"
"$REGISTRY_DIR"/*.md
"$REGISTRY_DIR"/*.json
)
if grep -RInEi "(password|passwd|pwd|token|secret|api[_-]?key|private[[:space:]_-]?key)[[:space:]]*[:=][[:space:]]*['\"]?[A-Za-z0-9_./+=-]{8,}" "${scan_files[@]}" >/tmp/registry_secret_like.$$ 2>/dev/null; then
fail "secret_like_value:$(cat /tmp/registry_secret_like.$$)"
fi
rm -f /tmp/registry_secret_like.$$
if grep -RInEi "(ФСТЭК|ФСБ).{0,80}(сертифицирован|сертификация|сертификат).{0,80}(есть|получен|имеется|подтвержден)|сертифицированное[[:space:]]+СЗИ" "${claim_scan_files[@]}" \
| grep -Eiv "(does not claim|not_claimed|forbidden_claim|не заявляет|не фиксируется|не является|не утверждает|нельзя)" \
>/tmp/registry_forbidden_fstec_fsb.$$ 2>/dev/null; then
fail "forbidden_claim_fstec_fsb_certification:$(cat /tmp/registry_forbidden_fstec_fsb.$$)"
fi
rm -f /tmp/registry_forbidden_fstec_fsb.$$
if grep -RInEi "(заменяет|replacement for|replaces).{0,80}(SIEM|DLP)|((SIEM|DLP).{0,80}(replacement|заменяет))" "${scan_files[@]}" \
| grep -Eiv "(не |not |does not|не является|не подменяет|не заявляет|forbidden|not_made)" \
if grep -RInEi "(заменяет|replacement for|replaces).{0,80}(SIEM|DLP)|((SIEM|DLP).{0,80}(replacement|заменяет))" "${claim_scan_files[@]}" \
| grep -Eiv "(не |not |does not|not_claimed|не является|не подменяет|не заявляет|forbidden|not_made)" \
>/tmp/registry_forbidden_replacement.$$ 2>/dev/null; then
fail "forbidden_claim_siem_dlp_replacement:$(cat /tmp/registry_forbidden_replacement.$$)"
fi
rm -f /tmp/registry_forbidden_replacement.$$
if grep -RInEi "(ML/LLM-based detection|LLM-based detection|ML-based detection|automatic remediation)" "${scan_files[@]}" \
| grep -Eiv "(forbidden|not_made|не заявляет|не фиксируется|не используется)" \
if grep -RInEi "(ML/LLM-based detection|LLM-based detection|ML-based detection|automatic remediation)" "${claim_scan_files[@]}" \
| grep -Eiv "(forbidden|not_made|not_claimed|не заявляет|не фиксируется|не используется|does not claim)" \
>/tmp/registry_forbidden_ai_auto.$$ 2>/dev/null; then
fail "forbidden_claim_ai_or_automatic_remediation:$(cat /tmp/registry_forbidden_ai_auto.$$)"
fi
rm -f /tmp/registry_forbidden_ai_auto.$$
if grep -RInEi "(юридически заверш(е|ё)нн?ая регистрация|принят.*в реестр|реестр.*заверш(е|ё)н)" "${claim_scan_files[@]}" \
| grep -Eiv "(не |нельзя|does not|not |не утверждает|не является|не подтверждает|forbidden)" \
>/tmp/registry_forbidden_legal_done.$$ 2>/dev/null; then
fail "forbidden_claim_legal_registry_completion:$(cat /tmp/registry_forbidden_legal_done.$$)"
fi
rm -f /tmp/registry_forbidden_legal_done.$$
if ((${#failures[@]} > 0)); then
printf 'registry_readiness_check=fail\n'
for failure in "${failures[@]}"; do