ops: move to forgejo-primary hosting with mirror-only codeberg/github

This commit is contained in:
every.channel 2026-02-28 00:48:12 -08:00
parent a5bc6c5226
commit 043b1730dc
No known key found for this signature in database
18 changed files with 336 additions and 66 deletions

View file

@ -5,7 +5,7 @@ root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "${root}"
in_file="${1:-secrets/token.txt}"
out_file="${2:-secrets/codeberg-token.age}"
out_file="${2:-secrets/forge-token.age}"
rules_file="${EVERY_CHANNEL_AGE_RULES_FILE:-${root}/secrets.nix}"
identity_file="${EVERY_CHANNEL_AGE_IDENTITY_FILE:-$HOME/.config/every.channel/keys/founder_ed25519}"

View file

@ -1,35 +1,6 @@
#!/usr/bin/env bash
set -euo pipefail
# Back-compat shim. Prefer `scripts/fj-auth-forge.sh`.
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "${root}"
# Forgejo CLI: `fj`
#
# Auth token source order:
# 1) CODEBERG_TOKEN env var
# 2) `agenix -d secrets/codeberg-token.age` (optional)
# 3) `age -d -i <identity> secrets/codeberg-token.age` (optional)
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 ]]; then
if command -v agenix >/dev/null 2>&1; then
export CODEBERG_TOKEN
CODEBERG_TOKEN="$(RULES="${rules_file}" agenix -d secrets/codeberg-token.age -i "${identity_file}")"
elif command -v age >/dev/null 2>&1; then
export CODEBERG_TOKEN
CODEBERG_TOKEN="$(age -d -i "${identity_file}" secrets/codeberg-token.age)"
fi
fi
if [[ -z "${CODEBERG_TOKEN:-}" ]]; then
echo "error: CODEBERG_TOKEN is not set" >&2
echo "hint: set CODEBERG_TOKEN or create secrets/codeberg-token.age via agenix" >&2
exit 2
fi
# Avoid passing the token on the command line (shows up in process listings); use stdin.
printf "%s" "${CODEBERG_TOKEN}" | fj -H https://codeberg.org auth add-key every-channel
echo "fj configured. Try: fj -H https://codeberg.org whoami"
exec "${root}/scripts/fj-auth-forge.sh"

53
scripts/fj-auth-forge.sh Executable file
View file

@ -0,0 +1,53 @@
#!/usr/bin/env bash
set -euo pipefail
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "${root}"
# Forgejo CLI: `fj`
#
# Auth token source order:
# 1) EVERY_CHANNEL_FORGE_TOKEN / FORGE_TOKEN / CODEBERG_TOKEN env var
# 2) `agenix -d secrets/forge-token.age` or `secrets/codeberg-token.age` (optional)
# 3) `age -d -i <identity> secrets/forge-token.age` or `secrets/codeberg-token.age` (optional)
host="${EVERY_CHANNEL_FORGE_HOST:-https://forge.every.channel}"
account="${EVERY_CHANNEL_FORGE_ACCOUNT:-every-channel}"
token_file_primary="${EVERY_CHANNEL_FORGE_TOKEN_FILE:-secrets/forge-token.age}"
token_file_compat="${EVERY_CHANNEL_CODEBERG_TOKEN_FILE:-secrets/codeberg-token.age}"
rules_file="${EVERY_CHANNEL_AGE_RULES_FILE:-./secrets.nix}"
identity_file="${EVERY_CHANNEL_AGE_IDENTITY_FILE:-$HOME/.config/every.channel/keys/founder_ed25519}"
token="${EVERY_CHANNEL_FORGE_TOKEN:-${FORGE_TOKEN:-${CODEBERG_TOKEN:-}}}"
load_token_from_file() {
local candidate="$1"
[[ -f "${candidate}" ]] || return 1
if command -v agenix >/dev/null 2>&1; then
RULES="${rules_file}" agenix -d "${candidate}" -i "${identity_file}" 2>/dev/null || return 1
return 0
fi
if command -v age >/dev/null 2>&1; then
age -d -i "${identity_file}" "${candidate}" 2>/dev/null || return 1
return 0
fi
return 1
}
if [[ -z "${token}" ]]; then
token="$(load_token_from_file "${token_file_primary}" || true)"
fi
if [[ -z "${token}" ]]; then
token="$(load_token_from_file "${token_file_compat}" || true)"
fi
if [[ -z "${token}" ]]; then
echo "error: forge token is not set" >&2
echo "hint: set EVERY_CHANNEL_FORGE_TOKEN/FORGE_TOKEN or create ${token_file_primary}" >&2
exit 2
fi
# Avoid passing the token on the command line (shows up in process listings); use stdin.
printf "%s" "${token}" | fj -H "${host}" auth add-key "${account}"
echo "fj configured. Try: fj -H ${host} whoami"

View file

@ -4,7 +4,7 @@ set -euo pipefail
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "${root}"
host="${EVERY_CHANNEL_FORGE_HOST:-https://codeberg.org}"
host="${EVERY_CHANNEL_FORGE_HOST:-https://forge.every.channel}"
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}"
@ -13,26 +13,35 @@ 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}"
token_file_primary="${EVERY_CHANNEL_FORGE_TOKEN_FILE:-secrets/forge-token.age}"
token_file_compat="${EVERY_CHANNEL_CODEBERG_TOKEN_FILE:-secrets/codeberg-token.age}"
if [[ -z "${CODEBERG_TOKEN:-}" && -f secrets/codeberg-token.age && -f "${identity_file}" ]]; then
token="${EVERY_CHANNEL_FORGE_TOKEN:-${FORGE_TOKEN:-${CODEBERG_TOKEN:-}}}"
load_token_from_file() {
local candidate="$1"
[[ -f "${candidate}" && -f "${identity_file}" ]] || return 1
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
RULES="${rules_file}" agenix -d "${candidate}" -i "${identity_file}" 2>/dev/null || return 1
return 0
fi
if command -v age >/dev/null 2>&1; then
age -d -i "${identity_file}" "${candidate}" 2>/dev/null || return 1
return 0
fi
return 1
}
if [[ -z "${token}" ]]; then
token="$(load_token_from_file "${token_file_primary}" || true)"
fi
if [[ -z "${token}" ]]; then
token="$(load_token_from_file "${token_file_compat}" || true)"
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
if [[ -z "${token}" ]]; then
echo "error: forge token is not set" >&2
echo "hint: set EVERY_CHANNEL_FORGE_TOKEN/FORGE_TOKEN or configure ${token_file_primary}" >&2
exit 2
fi
@ -90,18 +99,18 @@ JSON
)"
status="$(curl -sS -o /dev/null -w '%{http_code}' \
-H "Authorization: token ${CODEBERG_TOKEN}" \
-H "Authorization: token ${token}" \
"${api}/${branch}" || true)"
if [[ "${status}" == "404" ]]; then
curl -fsSL -X POST \
-H "Authorization: token ${CODEBERG_TOKEN}" \
-H "Authorization: token ${token}" \
-H "content-type: application/json" \
"${api}" \
-d "${payload}" >/dev/null
elif [[ "${status}" == "200" ]]; then
curl -fsSL -X PATCH \
-H "Authorization: token ${CODEBERG_TOKEN}" \
-H "Authorization: token ${token}" \
-H "content-type: application/json" \
"${api}/${branch}" \
-d "${payload}" >/dev/null
@ -111,7 +120,7 @@ else
fi
current="$(curl -fsSL \
-H "Authorization: token ${CODEBERG_TOKEN}" \
-H "Authorization: token ${token}" \
"${api}/${branch}")"
if ! printf '%s' "${current}" | rg -q '"enable_status_check":\s*true'; then

View file

@ -4,7 +4,7 @@ set -euo pipefail
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "${root}"
host="${EVERY_CHANNEL_FORGE_HOST:-https://codeberg.org}"
host="${EVERY_CHANNEL_FORGE_HOST:-https://forge.every.channel}"
repo="${EVERY_CHANNEL_FORGE_REPO:-every-channel/every.channel}"
secret_name="${EVERY_CHANNEL_FORGE_AGE_SECRET_NAME:-AGE_FORGE_SSH_KEY}"
key_path="${1:-$HOME/.config/every.channel/keys/founder_ed25519}"
@ -18,7 +18,7 @@ if ! command -v fj >/dev/null 2>&1; then
exit 2
fi
"${root}/scripts/fj-auth-codeberg.sh" >/dev/null
"${root}/scripts/fj-auth-forge.sh" >/dev/null
key_data="$(base64 < "${key_path}" | tr -d '\n')"
if [[ -z "${key_data}" ]]; then

View file

@ -0,0 +1,82 @@
#!/usr/bin/env bash
set -euo pipefail
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "${root}"
host="${EVERY_CHANNEL_FORGE_HOST:-https://forge.every.channel}"
repo="${EVERY_CHANNEL_FORGE_REPO:-every-channel/every.channel}"
enabled_raw="${EVERY_CHANNEL_FORGE_ACTIONS_ENABLED:-false}"
rules_file="${EVERY_CHANNEL_AGE_RULES_FILE:-./secrets.nix}"
identity_file="${EVERY_CHANNEL_AGE_IDENTITY_FILE:-$HOME/.config/every.channel/keys/founder_ed25519}"
token_file_primary="${EVERY_CHANNEL_FORGE_TOKEN_FILE:-secrets/forge-token.age}"
token_file_compat="${EVERY_CHANNEL_CODEBERG_TOKEN_FILE:-secrets/codeberg-token.age}"
token="${EVERY_CHANNEL_FORGE_TOKEN:-${FORGE_TOKEN:-${CODEBERG_TOKEN:-}}}"
load_token_from_file() {
local candidate="$1"
[[ -f "${candidate}" && -f "${identity_file}" ]] || return 1
if command -v agenix >/dev/null 2>&1; then
RULES="${rules_file}" agenix -d "${candidate}" -i "${identity_file}" 2>/dev/null || return 1
return 0
fi
if command -v age >/dev/null 2>&1; then
age -d -i "${identity_file}" "${candidate}" 2>/dev/null || return 1
return 0
fi
return 1
}
if [[ -z "${token}" ]]; then
token="$(load_token_from_file "${token_file_primary}" || true)"
fi
if [[ -z "${token}" ]]; then
token="$(load_token_from_file "${token_file_compat}" || true)"
fi
if [[ -z "${token}" ]]; then
echo "error: forge token is not set" >&2
echo "hint: set EVERY_CHANNEL_FORGE_TOKEN/FORGE_TOKEN or configure ${token_file_primary}" >&2
exit 2
fi
if [[ ! "${repo}" =~ ^[^/]+/[^/]+$ ]]; then
echo "error: EVERY_CHANNEL_FORGE_REPO must be '<owner>/<repo>' (got '${repo}')" >&2
exit 2
fi
enabled_lc="$(printf '%s' "${enabled_raw}" | tr '[:upper:]' '[:lower:]')"
enabled="false"
if [[ "${enabled_lc}" == "true" || "${enabled_raw}" == "1" ]]; then
enabled="true"
fi
owner="${repo%%/*}"
repo_name="${repo#*/}"
api="${host%/}/api/v1/repos/${owner}/${repo_name}"
payload="$(cat <<JSON
{
"has_actions": ${enabled}
}
JSON
)"
curl -fsSL -X PATCH \
-H "Authorization: token ${token}" \
-H "content-type: application/json" \
"${api}" \
-d "${payload}" >/dev/null
current="$(curl -fsSL \
-H "Authorization: token ${token}" \
"${api}")"
if ! printf '%s' "${current}" | rg -q "\"has_actions\":\\s*${enabled}"; then
echo "error: repository actions state did not update to ${enabled}" >&2
exit 2
fi
echo "ok: set has_actions=${enabled} for ${repo} on ${host}"

View file

@ -0,0 +1,38 @@
#!/usr/bin/env bash
set -euo pipefail
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "${root}"
primary_remote="${EVERY_CHANNEL_PRIMARY_REMOTE:-origin}"
primary_url="${EVERY_CHANNEL_PRIMARY_GIT_URL:-git@forge.every.channel:every-channel/every.channel.git}"
codeberg_remote="${EVERY_CHANNEL_CODEBERG_REMOTE:-mirror-codeberg}"
codeberg_url="${EVERY_CHANNEL_CODEBERG_GIT_URL:-git@codeberg.org:every-channel/every.channel.git}"
github_remote="${EVERY_CHANNEL_GITHUB_REMOTE:-mirror-github}"
github_url="${EVERY_CHANNEL_GITHUB_GIT_URL:-git@github.com:every-channel/every.channel.git}"
legacy_codeberg_remote="${EVERY_CHANNEL_LEGACY_CODEBERG_REMOTE:-codeberg}"
set_remote_url() {
local name="$1"
local url="$2"
if git remote get-url "${name}" >/dev/null 2>&1; then
git remote set-url "${name}" "${url}"
else
git remote add "${name}" "${url}"
fi
}
# If a legacy `codeberg` remote exists and mirror-codeberg does not, preserve it as a mirror remote.
if git remote get-url "${legacy_codeberg_remote}" >/dev/null 2>&1 && ! git remote get-url "${codeberg_remote}" >/dev/null 2>&1; then
git remote rename "${legacy_codeberg_remote}" "${codeberg_remote}"
fi
set_remote_url "${primary_remote}" "${primary_url}"
set_remote_url "${codeberg_remote}" "${codeberg_url}"
set_remote_url "${github_remote}" "${github_url}"
echo "ok: configured primary + mirror remotes"
git remote -v

23
scripts/git-push-mirrors.sh Executable file
View file

@ -0,0 +1,23 @@
#!/usr/bin/env bash
set -euo pipefail
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "${root}"
branch="${EVERY_CHANNEL_MIRROR_BRANCH:-$(git rev-parse --abbrev-ref HEAD)}"
push_tags="${EVERY_CHANNEL_MIRROR_PUSH_TAGS:-true}"
remotes="${EVERY_CHANNEL_MIRROR_REMOTES:-mirror-codeberg mirror-github}"
for remote in ${remotes}; do
if ! git remote get-url "${remote}" >/dev/null 2>&1; then
echo "warn: remote not configured, skipping: ${remote}" >&2
continue
fi
echo "sync: ${remote} (${branch})"
git push "${remote}" "${branch}:${branch}"
if [[ "${push_tags}" == "true" || "${push_tags}" == "1" ]]; then
git push "${remote}" --tags
fi
done
echo "ok: mirror push complete"