ops: move to forgejo-primary hosting with mirror-only codeberg/github
This commit is contained in:
parent
a5bc6c5226
commit
043b1730dc
18 changed files with 336 additions and 66 deletions
|
|
@ -8,6 +8,7 @@ on:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
checks:
|
checks:
|
||||||
|
if: ${{ github.server_url != 'https://codeberg.org' }}
|
||||||
runs-on: codeberg-medium-lazy
|
runs-on: codeberg-medium-lazy
|
||||||
steps:
|
steps:
|
||||||
- name: Fetch source (no git required)
|
- name: Fetch source (no git required)
|
||||||
|
|
@ -32,11 +33,19 @@ jobs:
|
||||||
echo "error: missing GITHUB_SHA"
|
echo "error: missing GITHUB_SHA"
|
||||||
exit 2
|
exit 2
|
||||||
fi
|
fi
|
||||||
|
if [[ -z "${GITHUB_SERVER_URL:-}" ]]; then
|
||||||
|
echo "error: missing GITHUB_SERVER_URL"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
if [[ -z "${GITHUB_REPOSITORY:-}" ]]; then
|
||||||
|
echo "error: missing GITHUB_REPOSITORY"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
rm -rf .repo
|
rm -rf .repo
|
||||||
mkdir -p .repo
|
mkdir -p .repo
|
||||||
curl -fsSL -H "Authorization: token ${GITHUB_TOKEN}" \
|
curl -fsSL -H "Authorization: token ${GITHUB_TOKEN}" \
|
||||||
"https://codeberg.org/api/v1/repos/every-channel/every.channel/archive/${GITHUB_SHA}.tar.gz?rev=${GITHUB_SHA}" \
|
"${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}/archive/${GITHUB_SHA}.tar.gz?rev=${GITHUB_SHA}" \
|
||||||
-o .repo/src.tgz
|
-o .repo/src.tgz
|
||||||
tar -xzf .repo/src.tgz -C .repo --strip-components=1
|
tar -xzf .repo/src.tgz -C .repo --strip-components=1
|
||||||
rm -f .repo/src.tgz
|
rm -f .repo/src.tgz
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ concurrency:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
checks:
|
checks:
|
||||||
|
if: ${{ github.server_url != 'https://codeberg.org' }}
|
||||||
runs-on: codeberg-medium-lazy
|
runs-on: codeberg-medium-lazy
|
||||||
steps:
|
steps:
|
||||||
- name: Fetch Source (no git required)
|
- name: Fetch Source (no git required)
|
||||||
|
|
@ -35,12 +36,20 @@ jobs:
|
||||||
echo "error: missing GITHUB_SHA"
|
echo "error: missing GITHUB_SHA"
|
||||||
exit 2
|
exit 2
|
||||||
fi
|
fi
|
||||||
|
if [[ -z "${GITHUB_SERVER_URL:-}" ]]; then
|
||||||
|
echo "error: missing GITHUB_SERVER_URL"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
if [[ -z "${GITHUB_REPOSITORY:-}" ]]; then
|
||||||
|
echo "error: missing GITHUB_REPOSITORY"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
rm -rf .repo
|
rm -rf .repo
|
||||||
mkdir -p .repo
|
mkdir -p .repo
|
||||||
|
|
||||||
curl -fsSL -H "Authorization: token ${GITHUB_TOKEN}" \
|
curl -fsSL -H "Authorization: token ${GITHUB_TOKEN}" \
|
||||||
"https://codeberg.org/api/v1/repos/every-channel/every.channel/archive/${GITHUB_SHA}.tar.gz?rev=${GITHUB_SHA}" \
|
"${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}/archive/${GITHUB_SHA}.tar.gz?rev=${GITHUB_SHA}" \
|
||||||
-o .repo/src.tgz
|
-o .repo/src.tgz
|
||||||
tar -xzf .repo/src.tgz -C .repo --strip-components=1
|
tar -xzf .repo/src.tgz -C .repo --strip-components=1
|
||||||
rm -f .repo/src.tgz
|
rm -f .repo/src.tgz
|
||||||
|
|
@ -111,6 +120,7 @@ jobs:
|
||||||
env -u NO_COLOR trunk build --release --public-url /
|
env -u NO_COLOR trunk build --release --public-url /
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
|
if: ${{ github.server_url != 'https://codeberg.org' }}
|
||||||
needs: checks
|
needs: checks
|
||||||
runs-on: codeberg-medium-lazy
|
runs-on: codeberg-medium-lazy
|
||||||
steps:
|
steps:
|
||||||
|
|
@ -136,13 +146,21 @@ jobs:
|
||||||
echo "error: missing GITHUB_SHA"
|
echo "error: missing GITHUB_SHA"
|
||||||
exit 2
|
exit 2
|
||||||
fi
|
fi
|
||||||
|
if [[ -z "${GITHUB_SERVER_URL:-}" ]]; then
|
||||||
|
echo "error: missing GITHUB_SERVER_URL"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
if [[ -z "${GITHUB_REPOSITORY:-}" ]]; then
|
||||||
|
echo "error: missing GITHUB_REPOSITORY"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
rm -rf .repo
|
rm -rf .repo
|
||||||
mkdir -p .repo
|
mkdir -p .repo
|
||||||
|
|
||||||
# Use the authenticated API archive endpoint (works for private repos).
|
# Use the authenticated API archive endpoint (works for private repos).
|
||||||
curl -fsSL -H "Authorization: token ${GITHUB_TOKEN}" \
|
curl -fsSL -H "Authorization: token ${GITHUB_TOKEN}" \
|
||||||
"https://codeberg.org/api/v1/repos/every-channel/every.channel/archive/${GITHUB_SHA}.tar.gz?rev=${GITHUB_SHA}" \
|
"${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}/archive/${GITHUB_SHA}.tar.gz?rev=${GITHUB_SHA}" \
|
||||||
-o .repo/src.tgz
|
-o .repo/src.tgz
|
||||||
tar -xzf .repo/src.tgz -C .repo --strip-components=1
|
tar -xzf .repo/src.tgz -C .repo --strip-components=1
|
||||||
rm -f .repo/src.tgz
|
rm -f .repo/src.tgz
|
||||||
|
|
@ -211,7 +229,7 @@ jobs:
|
||||||
cd .repo
|
cd .repo
|
||||||
curl -fsSL -X POST -H "Authorization: token ${GITHUB_TOKEN}" \
|
curl -fsSL -X POST -H "Authorization: token ${GITHUB_TOKEN}" \
|
||||||
-H "content-type: application/json" \
|
-H "content-type: application/json" \
|
||||||
"https://codeberg.org/api/v1/repos/every-channel/every.channel/statuses/${GITHUB_SHA}" \
|
"${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}/statuses/${GITHUB_SHA}" \
|
||||||
-d '{"context":"deploy-cloudflare/breadcrumb","state":"pending","description":"bootstrap ok"}' >/dev/null
|
-d '{"context":"deploy-cloudflare/breadcrumb","state":"pending","description":"bootstrap ok"}' >/dev/null
|
||||||
|
|
||||||
- name: Configure CI Age identity
|
- name: Configure CI Age identity
|
||||||
|
|
@ -236,7 +254,7 @@ jobs:
|
||||||
|
|
||||||
curl -fsSL -X POST -H "Authorization: token ${GITHUB_TOKEN}" \
|
curl -fsSL -X POST -H "Authorization: token ${GITHUB_TOKEN}" \
|
||||||
-H "content-type: application/json" \
|
-H "content-type: application/json" \
|
||||||
"https://codeberg.org/api/v1/repos/every-channel/every.channel/statuses/${GITHUB_SHA}" \
|
"${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}/statuses/${GITHUB_SHA}" \
|
||||||
-d '{"context":"deploy-cloudflare/breadcrumb","state":"pending","description":"age key ok"}' >/dev/null
|
-d '{"context":"deploy-cloudflare/breadcrumb","state":"pending","description":"age key ok"}' >/dev/null
|
||||||
|
|
||||||
- name: Decrypt CI secrets from repo
|
- name: Decrypt CI secrets from repo
|
||||||
|
|
@ -262,7 +280,7 @@ jobs:
|
||||||
|
|
||||||
curl -fsSL -X POST -H "Authorization: token ${GITHUB_TOKEN}" \
|
curl -fsSL -X POST -H "Authorization: token ${GITHUB_TOKEN}" \
|
||||||
-H "content-type: application/json" \
|
-H "content-type: application/json" \
|
||||||
"https://codeberg.org/api/v1/repos/every-channel/every.channel/statuses/${GITHUB_SHA}" \
|
"${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}/statuses/${GITHUB_SHA}" \
|
||||||
-d '{"context":"deploy-cloudflare/breadcrumb","state":"pending","description":"decrypt ok"}' >/dev/null
|
-d '{"context":"deploy-cloudflare/breadcrumb","state":"pending","description":"decrypt ok"}' >/dev/null
|
||||||
|
|
||||||
- name: Build site (web)
|
- name: Build site (web)
|
||||||
|
|
@ -301,7 +319,7 @@ jobs:
|
||||||
|
|
||||||
curl -fsSL -X POST -H "Authorization: token ${GITHUB_TOKEN}" \
|
curl -fsSL -X POST -H "Authorization: token ${GITHUB_TOKEN}" \
|
||||||
-H "content-type: application/json" \
|
-H "content-type: application/json" \
|
||||||
"https://codeberg.org/api/v1/repos/every-channel/every.channel/statuses/${GITHUB_SHA}" \
|
"${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}/statuses/${GITHUB_SHA}" \
|
||||||
-d '{"context":"deploy-cloudflare/breadcrumb","state":"pending","description":"build ok"}' >/dev/null
|
-d '{"context":"deploy-cloudflare/breadcrumb","state":"pending","description":"build ok"}' >/dev/null
|
||||||
|
|
||||||
- name: Deploy worker
|
- name: Deploy worker
|
||||||
|
|
@ -317,5 +335,5 @@ jobs:
|
||||||
|
|
||||||
curl -fsSL -X POST -H "Authorization: token ${GITHUB_TOKEN}" \
|
curl -fsSL -X POST -H "Authorization: token ${GITHUB_TOKEN}" \
|
||||||
-H "content-type: application/json" \
|
-H "content-type: application/json" \
|
||||||
"https://codeberg.org/api/v1/repos/every-channel/every.channel/statuses/${GITHUB_SHA}" \
|
"${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}/statuses/${GITHUB_SHA}" \
|
||||||
-d '{"context":"deploy-cloudflare/breadcrumb","state":"success","description":"deploy ok"}' >/dev/null
|
-d '{"context":"deploy-cloudflare/breadcrumb","state":"success","description":"deploy ok"}' >/dev/null
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ on:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
smoke:
|
smoke:
|
||||||
|
if: ${{ github.server_url != 'https://codeberg.org' }}
|
||||||
runs-on: codeberg-medium-lazy
|
runs-on: codeberg-medium-lazy
|
||||||
steps:
|
steps:
|
||||||
- name: Basic runner + secret smoke test
|
- name: Basic runner + secret smoke test
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,12 @@ Runbook:
|
||||||
cat docs/USAGE.md
|
cat docs/USAGE.md
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Git hosting topology:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cat docs/GIT_HOSTING.md
|
||||||
|
```
|
||||||
|
|
||||||
## WebTransport Watch (MoQ)
|
## WebTransport Watch (MoQ)
|
||||||
|
|
||||||
Publish (node -> Cloudflare relay):
|
Publish (node -> Cloudflare relay):
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# Branch Protection (Codeberg)
|
# Branch Protection (Forgejo Primary)
|
||||||
|
|
||||||
`main` should be protected to satisfy constitutional governance (`all changes merge through pull requests`) and to require CI before merge.
|
`main` should be protected to satisfy constitutional governance (`all changes merge through pull requests`) and to require CI before merge.
|
||||||
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
Optional overrides:
|
Optional overrides:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
EVERY_CHANNEL_FORGE_HOST=https://forge.every.channel \
|
||||||
EVERY_CHANNEL_FORGE_REPO=every-channel/every.channel \
|
EVERY_CHANNEL_FORGE_REPO=every-channel/every.channel \
|
||||||
EVERY_CHANNEL_PROTECTED_BRANCH=main \
|
EVERY_CHANNEL_PROTECTED_BRANCH=main \
|
||||||
EVERY_CHANNEL_REQUIRED_CHECKS="ci-gates / checks" \
|
EVERY_CHANNEL_REQUIRED_CHECKS="ci-gates / checks" \
|
||||||
|
|
@ -29,7 +30,8 @@ EVERY_CHANNEL_REQUIRED_APPROVALS=1 \
|
||||||
|
|
||||||
Token source order:
|
Token source order:
|
||||||
|
|
||||||
1. `CODEBERG_TOKEN` env var
|
1. `EVERY_CHANNEL_FORGE_TOKEN` / `FORGE_TOKEN` / `CODEBERG_TOKEN` env var
|
||||||
2. `secrets/codeberg-token.age` via `agenix` or `age`
|
2. `secrets/forge-token.age` (preferred) via `agenix` or `age`
|
||||||
|
3. `secrets/codeberg-token.age` (compat) via `agenix` or `age`
|
||||||
|
|
||||||
The token must have repository admin scope to edit branch protection.
|
The token must have repository admin scope to edit branch protection.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
# Cloudflare Deploy (Forgejo Actions)
|
# Cloudflare Deploy (Forgejo Actions)
|
||||||
|
|
||||||
This repo deploys `https://every.channel` via Wrangler.
|
This repo deploys `https://every.channel` via Wrangler.
|
||||||
|
The deploy workflow is intended to run on the primary Forgejo host (not Codeberg/GitHub mirrors).
|
||||||
|
|
||||||
## Prereqs
|
## Prereqs
|
||||||
|
|
||||||
|
|
@ -13,6 +14,10 @@ CI and deploy workflows:
|
||||||
- PR/main checks: `.forgejo/workflows/ci-gates.yml`
|
- PR/main checks: `.forgejo/workflows/ci-gates.yml`
|
||||||
- Deploy (main only, depends on checks): `.forgejo/workflows/deploy-cloudflare.yml`
|
- Deploy (main only, depends on checks): `.forgejo/workflows/deploy-cloudflare.yml`
|
||||||
|
|
||||||
|
Mirror behavior:
|
||||||
|
|
||||||
|
- Workflow jobs are guarded to skip execution on `https://codeberg.org`.
|
||||||
|
|
||||||
## Manual deploy (local)
|
## Manual deploy (local)
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|
|
||||||
45
docs/GIT_HOSTING.md
Normal file
45
docs/GIT_HOSTING.md
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
# Git Hosting Topology
|
||||||
|
|
||||||
|
Primary host:
|
||||||
|
|
||||||
|
- Forgejo (`origin`)
|
||||||
|
|
||||||
|
Mirrors (push-only):
|
||||||
|
|
||||||
|
- Codeberg (`mirror-codeberg`)
|
||||||
|
- GitHub (`mirror-github`)
|
||||||
|
|
||||||
|
Codeberg and GitHub are distribution mirrors only. CI/actions should run on Forgejo primary.
|
||||||
|
|
||||||
|
## Configure local remotes
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./scripts/git-configure-hosting.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Defaults:
|
||||||
|
|
||||||
|
- `origin`: `git@forge.every.channel:every-channel/every.channel.git`
|
||||||
|
- `mirror-codeberg`: `git@codeberg.org:every-channel/every.channel.git`
|
||||||
|
- `mirror-github`: `git@github.com:every-channel/every.channel.git`
|
||||||
|
|
||||||
|
You can override via env vars:
|
||||||
|
|
||||||
|
- `EVERY_CHANNEL_PRIMARY_GIT_URL`
|
||||||
|
- `EVERY_CHANNEL_CODEBERG_GIT_URL`
|
||||||
|
- `EVERY_CHANNEL_GITHUB_GIT_URL`
|
||||||
|
|
||||||
|
## Push mirrors
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./scripts/git-push-mirrors.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Disable actions on Codeberg mirror
|
||||||
|
|
||||||
|
```sh
|
||||||
|
EVERY_CHANNEL_FORGE_HOST=https://codeberg.org \
|
||||||
|
EVERY_CHANNEL_FORGE_REPO=every-channel/every.channel \
|
||||||
|
EVERY_CHANNEL_FORGE_ACTIONS_ENABLED=false \
|
||||||
|
./scripts/forge-set-repo-actions.sh
|
||||||
|
```
|
||||||
6
justfile
6
justfile
|
|
@ -6,6 +6,12 @@ default:
|
||||||
ecp-lint:
|
ecp-lint:
|
||||||
./scripts/ecp-lint.sh
|
./scripts/ecp-lint.sh
|
||||||
|
|
||||||
|
git-hosting:
|
||||||
|
./scripts/git-configure-hosting.sh
|
||||||
|
|
||||||
|
git-mirrors:
|
||||||
|
./scripts/git-push-mirrors.sh
|
||||||
|
|
||||||
test-core:
|
test-core:
|
||||||
cargo test -p ec-core -p ec-crypto -p ec-moq -p ec-iroh -p ec-linux-iptv
|
cargo test -p ec-core -p ec-crypto -p ec-moq -p ec-iroh -p ec-linux-iptv
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
cd "${root}"
|
cd "${root}"
|
||||||
|
|
||||||
in_file="${1:-secrets/token.txt}"
|
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}"
|
rules_file="${EVERY_CHANNEL_AGE_RULES_FILE:-${root}/secrets.nix}"
|
||||||
identity_file="${EVERY_CHANNEL_AGE_IDENTITY_FILE:-$HOME/.config/every.channel/keys/founder_ed25519}"
|
identity_file="${EVERY_CHANNEL_AGE_IDENTITY_FILE:-$HOME/.config/every.channel/keys/founder_ed25519}"
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,6 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Back-compat shim. Prefer `scripts/fj-auth-forge.sh`.
|
||||||
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
cd "${root}"
|
exec "${root}/scripts/fj-auth-forge.sh"
|
||||||
|
|
||||||
# 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"
|
|
||||||
|
|
|
||||||
53
scripts/fj-auth-forge.sh
Executable file
53
scripts/fj-auth-forge.sh
Executable 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"
|
||||||
|
|
@ -4,7 +4,7 @@ set -euo pipefail
|
||||||
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
cd "${root}"
|
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}"
|
repo="${EVERY_CHANNEL_FORGE_REPO:-every-channel/every.channel}"
|
||||||
branch="${EVERY_CHANNEL_PROTECTED_BRANCH:-main}"
|
branch="${EVERY_CHANNEL_PROTECTED_BRANCH:-main}"
|
||||||
required_checks_csv="${EVERY_CHANNEL_REQUIRED_CHECKS:-ci-gates / checks}"
|
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}"
|
rules_file="${EVERY_CHANNEL_AGE_RULES_FILE:-./secrets.nix}"
|
||||||
identity_file="${EVERY_CHANNEL_AGE_IDENTITY_FILE:-$HOME/.config/every.channel/keys/founder_ed25519}"
|
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
|
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)"
|
RULES="${rules_file}" agenix -d "${candidate}" -i "${identity_file}" 2>/dev/null || return 1
|
||||||
if [[ -n "${token}" ]]; then
|
return 0
|
||||||
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 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
|
fi
|
||||||
|
|
||||||
if [[ -z "${CODEBERG_TOKEN:-}" ]]; then
|
if [[ -z "${token}" ]]; then
|
||||||
echo "error: CODEBERG_TOKEN is not set" >&2
|
echo "error: forge token is not set" >&2
|
||||||
echo "hint: set CODEBERG_TOKEN or configure secrets/codeberg-token.age" >&2
|
echo "hint: set EVERY_CHANNEL_FORGE_TOKEN/FORGE_TOKEN or configure ${token_file_primary}" >&2
|
||||||
exit 2
|
exit 2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
@ -90,18 +99,18 @@ JSON
|
||||||
)"
|
)"
|
||||||
|
|
||||||
status="$(curl -sS -o /dev/null -w '%{http_code}' \
|
status="$(curl -sS -o /dev/null -w '%{http_code}' \
|
||||||
-H "Authorization: token ${CODEBERG_TOKEN}" \
|
-H "Authorization: token ${token}" \
|
||||||
"${api}/${branch}" || true)"
|
"${api}/${branch}" || true)"
|
||||||
|
|
||||||
if [[ "${status}" == "404" ]]; then
|
if [[ "${status}" == "404" ]]; then
|
||||||
curl -fsSL -X POST \
|
curl -fsSL -X POST \
|
||||||
-H "Authorization: token ${CODEBERG_TOKEN}" \
|
-H "Authorization: token ${token}" \
|
||||||
-H "content-type: application/json" \
|
-H "content-type: application/json" \
|
||||||
"${api}" \
|
"${api}" \
|
||||||
-d "${payload}" >/dev/null
|
-d "${payload}" >/dev/null
|
||||||
elif [[ "${status}" == "200" ]]; then
|
elif [[ "${status}" == "200" ]]; then
|
||||||
curl -fsSL -X PATCH \
|
curl -fsSL -X PATCH \
|
||||||
-H "Authorization: token ${CODEBERG_TOKEN}" \
|
-H "Authorization: token ${token}" \
|
||||||
-H "content-type: application/json" \
|
-H "content-type: application/json" \
|
||||||
"${api}/${branch}" \
|
"${api}/${branch}" \
|
||||||
-d "${payload}" >/dev/null
|
-d "${payload}" >/dev/null
|
||||||
|
|
@ -111,7 +120,7 @@ else
|
||||||
fi
|
fi
|
||||||
|
|
||||||
current="$(curl -fsSL \
|
current="$(curl -fsSL \
|
||||||
-H "Authorization: token ${CODEBERG_TOKEN}" \
|
-H "Authorization: token ${token}" \
|
||||||
"${api}/${branch}")"
|
"${api}/${branch}")"
|
||||||
|
|
||||||
if ! printf '%s' "${current}" | rg -q '"enable_status_check":\s*true'; then
|
if ! printf '%s' "${current}" | rg -q '"enable_status_check":\s*true'; then
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ set -euo pipefail
|
||||||
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
cd "${root}"
|
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}"
|
repo="${EVERY_CHANNEL_FORGE_REPO:-every-channel/every.channel}"
|
||||||
secret_name="${EVERY_CHANNEL_FORGE_AGE_SECRET_NAME:-AGE_FORGE_SSH_KEY}"
|
secret_name="${EVERY_CHANNEL_FORGE_AGE_SECRET_NAME:-AGE_FORGE_SSH_KEY}"
|
||||||
key_path="${1:-$HOME/.config/every.channel/keys/founder_ed25519}"
|
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
|
exit 2
|
||||||
fi
|
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')"
|
key_data="$(base64 < "${key_path}" | tr -d '\n')"
|
||||||
if [[ -z "${key_data}" ]]; then
|
if [[ -z "${key_data}" ]]; then
|
||||||
|
|
|
||||||
82
scripts/forge-set-repo-actions.sh
Executable file
82
scripts/forge-set-repo-actions.sh
Executable 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}"
|
||||||
38
scripts/git-configure-hosting.sh
Executable file
38
scripts/git-configure-hosting.sh
Executable 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
23
scripts/git-push-mirrors.sh
Executable 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"
|
||||||
|
|
@ -6,5 +6,6 @@ let
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
"secrets/cloudflare-api-token.age".publicKeys = [ founder forge ];
|
"secrets/cloudflare-api-token.age".publicKeys = [ founder forge ];
|
||||||
|
"secrets/forge-token.age".publicKeys = [ founder forge ];
|
||||||
"secrets/codeberg-token.age".publicKeys = [ founder forge ];
|
"secrets/codeberg-token.age".publicKeys = [ founder forge ];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,8 @@ nix develop -c ./scripts/fj-set-age-key-secret.sh ~/.config/every.channel/keys/f
|
||||||
|
|
||||||
- `secrets/secrets.nix`: recipients + secret file mapping
|
- `secrets/secrets.nix`: recipients + secret file mapping
|
||||||
- `secrets/cloudflare-api-token.age`: encrypted Cloudflare API token (used by deploy workflow)
|
- `secrets/cloudflare-api-token.age`: encrypted Cloudflare API token (used by deploy workflow)
|
||||||
- `secrets/codeberg-token.age`: encrypted Codeberg/Forgejo token for `fj` (optional)
|
- `secrets/forge-token.age`: encrypted Forgejo API token for admin scripts (optional, preferred)
|
||||||
|
- `secrets/codeberg-token.age`: encrypted Codeberg token for compatibility/mirror admin scripts (optional)
|
||||||
|
|
||||||
## Create / edit secrets (local)
|
## Create / edit secrets (local)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue