189 lines
6.4 KiB
Bash
Executable File
189 lines
6.4 KiB
Bash
Executable File
#!/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
|