#!/usr/bin/env bash set -euo pipefail set +x # Append two verified JSON receipts to a dedicated Gitea audit branch without # merging the shallow workflow checkout. Authentication is supplied by the # caller through GIT_ASKPASS; this script never accepts a token argument. required_env=( GITEA_AUDIT_REMOTE_URL GITEA_AUDIT_BRANCH GITEA_AUDIT_CONTROLLER_SOURCE GITEA_AUDIT_VERIFIER_SOURCE GITEA_AUDIT_CONTROLLER_TARGET GITEA_AUDIT_VERIFIER_TARGET GITEA_AUDIT_COMMIT_MESSAGE ) for name in "${required_env[@]}"; do if [ -z "${!name:-}" ]; then echo "BLOCKER audit_writeback_missing_${name}" exit 2 fi done if [ "${GITEA_AUDIT_REMOTE_URL}" = "http://192.168.0.110:3001/wooo/awoooi.git" ]; then if [ -z "${GIT_ASKPASS:-}" ] || [ ! -x "${GIT_ASKPASS}" ]; then echo "BLOCKER audit_writeback_askpass_missing_or_not_executable" exit 2 fi elif [[ "${GITEA_AUDIT_REMOTE_URL}" == file://* ]] && \ [ "${GITEA_AUDIT_TEST_FILE_REMOTE:-0}" = "1" ]; then : else echo "BLOCKER audit_writeback_remote_not_allowlisted" exit 2 fi if ! [[ "${GITEA_AUDIT_BRANCH}" =~ ^mcp-(artifact|replay)-receipts$ ]]; then echo "BLOCKER audit_writeback_branch_not_allowlisted" exit 2 fi if ! [[ "${GITEA_AUDIT_CONTROLLER_TARGET}" =~ ^docs/operations/external-mcp-(artifacts|replays)/[A-Za-z0-9._-]+-controller\.snapshot\.json$ ]]; then echo "BLOCKER audit_writeback_controller_target_not_allowlisted" exit 2 fi if ! [[ "${GITEA_AUDIT_VERIFIER_TARGET}" =~ ^docs/operations/external-mcp-(artifacts|replays)/[A-Za-z0-9._-]+-verifier\.snapshot\.json$ ]]; then echo "BLOCKER audit_writeback_verifier_target_not_allowlisted" exit 2 fi if [ "$(dirname "${GITEA_AUDIT_CONTROLLER_TARGET}")" != "$(dirname "${GITEA_AUDIT_VERIFIER_TARGET}")" ]; then echo "BLOCKER audit_writeback_target_directory_mismatch" exit 2 fi case "${GITEA_AUDIT_BRANCH}" in mcp-artifact-receipts) expected_target_directory="docs/operations/external-mcp-artifacts" expected_controller_schema="awoooi_external_mcp_artifact_receipt_v1" expected_verifier_schema="awoooi_external_mcp_artifact_verifier_receipt_v1" ;; mcp-replay-receipts) expected_target_directory="docs/operations/external-mcp-replays" expected_controller_schema="awoooi_external_mcp_replay_receipt_v1" expected_verifier_schema="awoooi_external_mcp_replay_verifier_receipt_v1" ;; esac if [ "$(dirname "${GITEA_AUDIT_CONTROLLER_TARGET}")" != "${expected_target_directory}" ]; then echo "BLOCKER audit_writeback_branch_target_mismatch" exit 2 fi if [ "${#GITEA_AUDIT_COMMIT_MESSAGE}" -gt 200 ] || \ [[ "${GITEA_AUDIT_COMMIT_MESSAGE}" == *$'\n'* ]] || \ [[ "${GITEA_AUDIT_COMMIT_MESSAGE}" == *$'\r'* ]]; then echo "BLOCKER audit_writeback_commit_message_invalid" exit 2 fi for source_path in \ "${GITEA_AUDIT_CONTROLLER_SOURCE}" \ "${GITEA_AUDIT_VERIFIER_SOURCE}"; do if [ ! -f "${source_path}" ] || [ -L "${source_path}" ]; then echo "BLOCKER audit_writeback_source_invalid" exit 2 fi source_size="$(wc -c < "${source_path}" | tr -d ' ')" if [ "${source_size}" -le 0 ] || [ "${source_size}" -gt 4194304 ]; then echo "BLOCKER audit_writeback_source_size_invalid" exit 2 fi done python3 - \ "${GITEA_AUDIT_CONTROLLER_SOURCE}" \ "${GITEA_AUDIT_VERIFIER_SOURCE}" \ "${expected_controller_schema}" \ "${expected_verifier_schema}" <<'PY' import json import sys from pathlib import Path def fail(message: str) -> None: print(message) raise SystemExit(2) try: receipts = [ json.loads(Path(value).read_text(encoding="utf-8")) for value in sys.argv[1:3] ] except (OSError, UnicodeError, json.JSONDecodeError): fail("BLOCKER audit_writeback_receipt_json_invalid") expected_schemas = sys.argv[3:5] identity_fields = ("run_id", "trace_id", "work_item_id") for payload, expected_schema in zip(receipts, expected_schemas): if not isinstance(payload, dict) or payload.get("schema_version") != expected_schema: fail("BLOCKER audit_writeback_receipt_schema_invalid") if any(not str(payload.get(field) or "") for field in identity_fields): fail("BLOCKER audit_writeback_receipt_identity_missing") if any(receipts[0][field] != receipts[1][field] for field in identity_fields): fail("BLOCKER audit_writeback_receipt_identity_mismatch") PY remote_name="gitea-audit" git remote remove "${remote_name}" >/dev/null 2>&1 || true git remote add "${remote_name}" "${GITEA_AUDIT_REMOTE_URL}" rm -f \ "${GITEA_AUDIT_CONTROLLER_TARGET}" \ "${GITEA_AUDIT_VERIFIER_TARGET}" for attempt in 1 2 3; do if git ls-remote --exit-code --heads "${remote_name}" \ "refs/heads/${GITEA_AUDIT_BRANCH}" >/dev/null 2>&1; then git fetch --no-tags --depth=1 "${remote_name}" "${GITEA_AUDIT_BRANCH}" git checkout --force --detach FETCH_HEAD fi target_directory="$(dirname "${GITEA_AUDIT_CONTROLLER_TARGET}")" for ancestor in docs docs/operations "${target_directory}"; do if [ -L "${ancestor}" ]; then echo "BLOCKER audit_writeback_target_symlink_rejected" exit 2 fi done mkdir -p "${target_directory}" for target_path in \ "${GITEA_AUDIT_CONTROLLER_TARGET}" \ "${GITEA_AUDIT_VERIFIER_TARGET}"; do if [ -L "${target_path}" ]; then echo "BLOCKER audit_writeback_target_symlink_rejected" exit 2 fi done install -m 0644 \ "${GITEA_AUDIT_CONTROLLER_SOURCE}" \ "${GITEA_AUDIT_CONTROLLER_TARGET}" install -m 0644 \ "${GITEA_AUDIT_VERIFIER_SOURCE}" \ "${GITEA_AUDIT_VERIFIER_TARGET}" git add \ "${GITEA_AUDIT_CONTROLLER_TARGET}" \ "${GITEA_AUDIT_VERIFIER_TARGET}" while IFS= read -r staged_path; do if [ "${staged_path}" != "${GITEA_AUDIT_CONTROLLER_TARGET}" ] && \ [ "${staged_path}" != "${GITEA_AUDIT_VERIFIER_TARGET}" ]; then echo "BLOCKER audit_writeback_unexpected_staged_path" exit 2 fi done < <(git diff --cached --name-only) if git diff --cached --quiet; then echo "receipt_writeback=no_change" echo "receipt_branch=${GITEA_AUDIT_BRANCH}" echo "receipt_commit_sha=$(git rev-parse HEAD)" exit 0 fi git commit -m "${GITEA_AUDIT_COMMIT_MESSAGE}" if git push "${remote_name}" "HEAD:refs/heads/${GITEA_AUDIT_BRANCH}"; then echo "receipt_writeback=verified_audit_branch_normal_push" echo "receipt_branch=${GITEA_AUDIT_BRANCH}" echo "receipt_commit_sha=$(git rev-parse HEAD)" exit 0 fi echo "receipt_writeback_retry=${attempt}" sleep 5 done echo "BLOCKER receipt_writeback_non_fast_forward_after_bounded_retry" exit 1