#!/usr/bin/env bash set -euo pipefail root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "${root}" host="${EVERY_CHANNEL_FORGE_HOST:-https://codeberg.org}" repo="${EVERY_CHANNEL_FORGE_REPO:-every-channel/every.channel}" branch="${EVERY_CHANNEL_PROTECTED_BRANCH:-main}" required_checks_csv="${EVERY_CHANNEL_REQUIRED_CHECKS:-ci-gates / checks}" required_approvals="${EVERY_CHANNEL_REQUIRED_APPROVALS:-1}" require_signed_commits_raw="${EVERY_CHANNEL_REQUIRE_SIGNED_COMMITS:-true}" rules_file="${EVERY_CHANNEL_AGE_RULES_FILE:-./secrets.nix}" identity_file="${EVERY_CHANNEL_AGE_IDENTITY_FILE:-$HOME/.config/every.channel/keys/founder_ed25519}" if [[ -z "${CODEBERG_TOKEN:-}" && -f secrets/codeberg-token.age && -f "${identity_file}" ]]; then if command -v agenix >/dev/null 2>&1; then token="$(RULES="${rules_file}" agenix -d secrets/codeberg-token.age -i "${identity_file}" 2>/dev/null || true)" if [[ -n "${token}" ]]; then export CODEBERG_TOKEN CODEBERG_TOKEN="${token}" fi elif command -v age >/dev/null 2>&1; then token="$(age -d -i "${identity_file}" secrets/codeberg-token.age 2>/dev/null || true)" if [[ -n "${token}" ]]; then export CODEBERG_TOKEN CODEBERG_TOKEN="${token}" fi fi fi if [[ -z "${CODEBERG_TOKEN:-}" ]]; then echo "error: CODEBERG_TOKEN is not set" >&2 echo "hint: set CODEBERG_TOKEN or configure secrets/codeberg-token.age" >&2 exit 2 fi if [[ ! "${repo}" =~ ^[^/]+/[^/]+$ ]]; then echo "error: EVERY_CHANNEL_FORGE_REPO must be '/' (got '${repo}')" >&2 exit 2 fi case "${required_approvals}" in ''|*[!0-9]*) echo "error: EVERY_CHANNEL_REQUIRED_APPROVALS must be a non-negative integer" >&2 exit 2 ;; esac require_signed_commits_raw_lc="$(printf '%s' "${require_signed_commits_raw}" | tr '[:upper:]' '[:lower:]')" require_signed_commits="false" if [[ "${require_signed_commits_raw_lc}" == "true" || "${require_signed_commits_raw}" == "1" ]]; then require_signed_commits="true" fi owner="${repo%%/*}" repo_name="${repo#*/}" api="${host%/}/api/v1/repos/${owner}/${repo_name}/branch_protections" contexts_json="" IFS=',' read -r -a contexts <<< "${required_checks_csv}" for ctx in "${contexts[@]}"; do trimmed="$(echo "${ctx}" | sed -E 's/^[[:space:]]+//; s/[[:space:]]+$//')" [[ -n "${trimmed}" ]] || continue escaped="$(printf '%s' "${trimmed}" | sed 's/\\/\\\\/g; s/"/\\"/g')" if [[ -n "${contexts_json}" ]]; then contexts_json+=", " fi contexts_json+="\"${escaped}\"" done if [[ -z "${contexts_json}" ]]; then echo "error: no required status checks specified (EVERY_CHANNEL_REQUIRED_CHECKS)" >&2 exit 2 fi payload="$(cat </dev/null elif [[ "${status}" == "200" ]]; then curl -fsSL -X PATCH \ -H "Authorization: token ${CODEBERG_TOKEN}" \ -H "content-type: application/json" \ "${api}/${branch}" \ -d "${payload}" >/dev/null else echo "error: unexpected status while reading branch protection: ${status}" >&2 exit 2 fi current="$(curl -fsSL \ -H "Authorization: token ${CODEBERG_TOKEN}" \ "${api}/${branch}")" if ! printf '%s' "${current}" | rg -q '"enable_status_check":\s*true'; then echo "error: branch protection update did not enable status checks" >&2 exit 2 fi if ! printf '%s' "${current}" | rg -q "\"required_approvals\":\\s*${required_approvals}"; then echo "error: branch protection update did not set required approvals to ${required_approvals}" >&2 exit 2 fi for ctx in "${contexts[@]}"; do trimmed="$(echo "${ctx}" | sed -E 's/^[[:space:]]+//; s/[[:space:]]+$//')" [[ -n "${trimmed}" ]] || continue if ! printf '%s' "${current}" | rg -F -q "\"${trimmed}\""; then echo "error: required status check context missing after update: ${trimmed}" >&2 exit 2 fi done echo "ok: enforced branch protection for ${repo}:${branch}" echo "ok: required checks: ${required_checks_csv}" echo "ok: required approvals: ${required_approvals}"