Rotate operator secrets into agenix and deepen caches
This commit is contained in:
parent
7039bf5aad
commit
03415e579b
28 changed files with 526 additions and 126 deletions
|
|
@ -75,14 +75,18 @@ jobs:
|
|||
cache_root="${NSC_CACHE_PATH:-${HOME}/.cache/burrow}"
|
||||
mkdir -p \
|
||||
"${cache_root}/cargo" \
|
||||
"${cache_root}/cargo-target/${{ matrix.cache-id }}" \
|
||||
"${cache_root}/rustup" \
|
||||
"${cache_root}/sccache" \
|
||||
"${cache_root}/homebrew" \
|
||||
"${cache_root}/apple/PackageCache" \
|
||||
"${cache_root}/apple/SourcePackages" \
|
||||
"${cache_root}/apple/DerivedData/${{ matrix.cache-id }}"
|
||||
echo "CARGO_HOME=${cache_root}/cargo" >> "${GITHUB_ENV}"
|
||||
echo "CARGO_TARGET_DIR=${cache_root}/cargo-target/${{ matrix.cache-id }}" >> "${GITHUB_ENV}"
|
||||
echo "RUSTUP_HOME=${cache_root}/rustup" >> "${GITHUB_ENV}"
|
||||
echo "SCCACHE_DIR=${cache_root}/sccache" >> "${GITHUB_ENV}"
|
||||
echo "HOMEBREW_CACHE=${cache_root}/homebrew" >> "${GITHUB_ENV}"
|
||||
echo "APPLE_PACKAGE_CACHE=${cache_root}/apple/PackageCache" >> "${GITHUB_ENV}"
|
||||
echo "APPLE_SOURCE_PACKAGES=${cache_root}/apple/SourcePackages" >> "${GITHUB_ENV}"
|
||||
echo "APPLE_DERIVED_DATA=${cache_root}/apple/DerivedData/${{ matrix.cache-id }}" >> "${GITHUB_ENV}"
|
||||
|
|
|
|||
|
|
@ -33,9 +33,10 @@ jobs:
|
|||
run: |
|
||||
set -euo pipefail
|
||||
cache_root="${HOME}/.cache/burrow"
|
||||
mkdir -p "${cache_root}/cargo" "${cache_root}/sccache"
|
||||
mkdir -p "${cache_root}/cargo" "${cache_root}/sccache" "${cache_root}/cargo-target/build-rust"
|
||||
echo "CARGO_HOME=${cache_root}/cargo" >> "${GITHUB_ENV}"
|
||||
echo "SCCACHE_DIR=${cache_root}/sccache" >> "${GITHUB_ENV}"
|
||||
echo "CARGO_TARGET_DIR=${cache_root}/cargo-target/build-rust" >> "${GITHUB_ENV}"
|
||||
|
||||
- name: Test
|
||||
shell: bash
|
||||
|
|
|
|||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -12,6 +12,7 @@ target/
|
|||
.idea/
|
||||
|
||||
tmp/
|
||||
intake/
|
||||
|
||||
*.db
|
||||
*.sqlite3
|
||||
|
|
|
|||
|
|
@ -74,16 +74,17 @@ CARGO_PATH="$(dirname $PROTOC):$CARGO_PATH"
|
|||
# Run cargo without the various environment variables set by Xcode.
|
||||
# Those variables can confuse cargo and the build scripts it runs.
|
||||
EXTRA_ENV=()
|
||||
for VAR_NAME in HOME CARGO_HOME RUSTUP_HOME RUSTC_WRAPPER SCCACHE_DIR CARGO_INCREMENTAL; do
|
||||
for VAR_NAME in HOME CARGO_HOME CARGO_TARGET_DIR RUSTUP_HOME RUSTC_WRAPPER SCCACHE_DIR CARGO_INCREMENTAL; do
|
||||
if [[ -n "${!VAR_NAME:-}" ]]; then
|
||||
EXTRA_ENV+=("${VAR_NAME}=${!VAR_NAME}")
|
||||
fi
|
||||
done
|
||||
env -i PATH="$CARGO_PATH" PROTOC="$PROTOC" CARGO_TARGET_DIR="${CONFIGURATION_TEMP_DIR}/target" IPHONEOS_DEPLOYMENT_TARGET="$IPHONEOS_DEPLOYMENT_TARGET" MACOSX_DEPLOYMENT_TARGET="$MACOSX_DEPLOYMENT_TARGET" "${EXTRA_ENV[@]}" cargo build "${CARGO_ARGS[@]}"
|
||||
EFFECTIVE_CARGO_TARGET_DIR="${CARGO_TARGET_DIR:-${CONFIGURATION_TEMP_DIR}/target}"
|
||||
env -i PATH="$CARGO_PATH" PROTOC="$PROTOC" CARGO_TARGET_DIR="${EFFECTIVE_CARGO_TARGET_DIR}" IPHONEOS_DEPLOYMENT_TARGET="$IPHONEOS_DEPLOYMENT_TARGET" MACOSX_DEPLOYMENT_TARGET="$MACOSX_DEPLOYMENT_TARGET" "${EXTRA_ENV[@]}" cargo build "${CARGO_ARGS[@]}"
|
||||
|
||||
mkdir -p "${BUILT_PRODUCTS_DIR}"
|
||||
|
||||
# Use `lipo` to merge the architectures together into BUILT_PRODUCTS_DIR
|
||||
/usr/bin/xcrun --sdk $PLATFORM_NAME lipo \
|
||||
-create $(printf "${CONFIGURATION_TEMP_DIR}/target/%q/${CARGO_TARGET_SUBDIR}/libburrow.a " "${RUST_TARGETS[@]}") \
|
||||
-create $(printf "${EFFECTIVE_CARGO_TARGET_DIR}/%q/${CARGO_TARGET_SUBDIR}/libburrow.a " "${RUST_TARGETS[@]}") \
|
||||
-output "${BUILT_PRODUCTS_DIR}/libburrow.a"
|
||||
|
|
|
|||
7
Makefile
7
Makefile
|
|
@ -5,7 +5,12 @@ SECRETS := forgejo/admin-password \
|
|||
forgejo/agent-ssh-key \
|
||||
forgejo/nsc-token \
|
||||
forgejo/nsc-dispatcher-config \
|
||||
forgejo/nsc-autoscaler-config
|
||||
forgejo/nsc-autoscaler-config \
|
||||
cloudflare/api-token \
|
||||
hetzner/api-token \
|
||||
forwardemail/api-token \
|
||||
forwardemail/hetzner-s3-user \
|
||||
forwardemail/hetzner-s3-secret
|
||||
|
||||
tun := $(shell ifconfig -l | sed 's/ /\n/g' | grep utun | tail -n 1)
|
||||
cargo_console := env RUST_BACKTRACE=1 RUST_LOG=debug RUSTFLAGS='--cfg tokio_unstable' cargo run --all-features --
|
||||
|
|
|
|||
78
Scripts/_burrow-secrets.sh
Normal file
78
Scripts/_burrow-secrets.sh
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
BURROW_SECRET_TMPFILES=()
|
||||
|
||||
burrow_cleanup_secret_tmpfiles() {
|
||||
local path
|
||||
for path in "${BURROW_SECRET_TMPFILES[@]:-}"; do
|
||||
[[ -n "${path}" ]] && rm -f "${path}" >/dev/null 2>&1 || true
|
||||
done
|
||||
BURROW_SECRET_TMPFILES=()
|
||||
}
|
||||
|
||||
burrow_decrypt_age_secret_to_temp() {
|
||||
local repo_root="$1"
|
||||
local secret_path="$2"
|
||||
local tmp_file
|
||||
|
||||
if [[ ! -f "${secret_path}" ]]; then
|
||||
echo "age secret not found: ${secret_path}" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
tmp_file="$(mktemp "${TMPDIR:-/tmp}/burrow-secret.XXXXXX")"
|
||||
nix --extra-experimental-features "nix-command flakes" run "${repo_root}#agenix" -- -d "${secret_path}" > "${tmp_file}"
|
||||
chmod 600 "${tmp_file}"
|
||||
BURROW_SECRET_TMPFILES+=("${tmp_file}")
|
||||
printf '%s\n' "${tmp_file}"
|
||||
}
|
||||
|
||||
burrow_resolve_secret_file() {
|
||||
local repo_root="$1"
|
||||
local explicit_path="$2"
|
||||
local intake_path="$3"
|
||||
local age_path="$4"
|
||||
local fallback_path="${5:-}"
|
||||
|
||||
if [[ -n "${explicit_path}" ]]; then
|
||||
if [[ ! -s "${explicit_path}" ]]; then
|
||||
echo "required file missing or empty: ${explicit_path}" >&2
|
||||
return 1
|
||||
fi
|
||||
printf '%s\n' "${explicit_path}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ -n "${intake_path}" && -s "${intake_path}" ]]; then
|
||||
printf '%s\n' "${intake_path}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ -n "${age_path}" && -f "${age_path}" ]]; then
|
||||
burrow_decrypt_age_secret_to_temp "${repo_root}" "${age_path}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ -n "${fallback_path}" && -s "${fallback_path}" ]]; then
|
||||
printf '%s\n' "${fallback_path}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
burrow_encrypt_secret_from_file() {
|
||||
local repo_root="$1"
|
||||
local secret_path="$2"
|
||||
local source_path="$3"
|
||||
|
||||
if [[ ! -s "${source_path}" ]]; then
|
||||
echo "secret source missing or empty: ${source_path}" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
SECRET_SOURCE_FILE="${source_path}" \
|
||||
EDITOR="${repo_root}/Scripts/agenix-load-file.sh" \
|
||||
nix --extra-experimental-features "nix-command flakes" run "${repo_root}#agenix" -- -e "${secret_path}"
|
||||
}
|
||||
|
|
@ -3,6 +3,8 @@ set -euo pipefail
|
|||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
# shellcheck source=Scripts/_burrow-secrets.sh
|
||||
source "${SCRIPT_DIR}/_burrow-secrets.sh"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
|
|
@ -10,27 +12,33 @@ Usage: Scripts/bootstrap-forge-intake.sh [options]
|
|||
|
||||
Copy the minimum Burrow forge bootstrap secrets onto the target host under
|
||||
/var/lib/burrow/intake with the ownership expected by the NixOS services.
|
||||
Legacy path only: the current forge runtime consumes agenix secrets directly.
|
||||
|
||||
Options:
|
||||
--host <user@host> SSH target (default: root@git.burrow.net)
|
||||
--ssh-key <path> SSH private key used to reach the host
|
||||
(default: intake/agent_at_burrow_net_ed25519)
|
||||
(default: secrets/forgejo/agent-ssh-key.age, then intake/)
|
||||
--password-file <path> Forgejo admin bootstrap password file
|
||||
(default: intake/forgejo_pass_contact_at_burrow_net.txt)
|
||||
(default: secrets/forgejo/admin-password.age, then intake/)
|
||||
--agent-key-file <path> Agent SSH private key copied for runner bootstrap
|
||||
(default: intake/agent_at_burrow_net_ed25519)
|
||||
(default: secrets/forgejo/agent-ssh-key.age, then intake/)
|
||||
--no-verify Skip remote ls/stat verification after install
|
||||
-h, --help Show this help text
|
||||
EOF
|
||||
}
|
||||
|
||||
HOST="${BURROW_FORGE_HOST:-root@git.burrow.net}"
|
||||
SSH_KEY="${BURROW_FORGE_SSH_KEY:-${REPO_ROOT}/intake/agent_at_burrow_net_ed25519}"
|
||||
PASSWORD_FILE="${BURROW_FORGE_PASSWORD_FILE:-${REPO_ROOT}/intake/forgejo_pass_contact_at_burrow_net.txt}"
|
||||
AGENT_KEY_FILE="${BURROW_FORGE_AGENT_KEY_FILE:-${REPO_ROOT}/intake/agent_at_burrow_net_ed25519}"
|
||||
SSH_KEY="${BURROW_FORGE_SSH_KEY:-}"
|
||||
PASSWORD_FILE="${BURROW_FORGE_PASSWORD_FILE:-}"
|
||||
AGENT_KEY_FILE="${BURROW_FORGE_AGENT_KEY_FILE:-}"
|
||||
KNOWN_HOSTS_FILE="${BURROW_FORGE_KNOWN_HOSTS_FILE:-${HOME}/.cache/burrow/forge-known_hosts}"
|
||||
VERIFY=1
|
||||
|
||||
cleanup() {
|
||||
burrow_cleanup_secret_tmpfiles
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--host)
|
||||
|
|
@ -67,12 +75,29 @@ done
|
|||
|
||||
mkdir -p "$(dirname "${KNOWN_HOSTS_FILE}")"
|
||||
|
||||
for path in "${SSH_KEY}" "${PASSWORD_FILE}" "${AGENT_KEY_FILE}"; do
|
||||
if [[ ! -s "${path}" ]]; then
|
||||
echo "required file missing or empty: ${path}" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
SSH_KEY="$(
|
||||
burrow_resolve_secret_file \
|
||||
"${REPO_ROOT}" \
|
||||
"${SSH_KEY}" \
|
||||
"${REPO_ROOT}/intake/agent_at_burrow_net_ed25519" \
|
||||
"${REPO_ROOT}/secrets/forgejo/agent-ssh-key.age" \
|
||||
"${HOME}/.ssh/agent_at_burrow_net_ed25519"
|
||||
)"
|
||||
PASSWORD_FILE="$(
|
||||
burrow_resolve_secret_file \
|
||||
"${REPO_ROOT}" \
|
||||
"${PASSWORD_FILE}" \
|
||||
"${REPO_ROOT}/intake/forgejo_pass_contact_at_burrow_net.txt" \
|
||||
"${REPO_ROOT}/secrets/forgejo/admin-password.age"
|
||||
)"
|
||||
AGENT_KEY_FILE="$(
|
||||
burrow_resolve_secret_file \
|
||||
"${REPO_ROOT}" \
|
||||
"${AGENT_KEY_FILE}" \
|
||||
"${REPO_ROOT}/intake/agent_at_burrow_net_ed25519" \
|
||||
"${REPO_ROOT}/secrets/forgejo/agent-ssh-key.age" \
|
||||
"${HOME}/.ssh/agent_at_burrow_net_ed25519"
|
||||
)"
|
||||
|
||||
ssh_opts=(
|
||||
-i "${SSH_KEY}"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ set -euo pipefail
|
|||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
# shellcheck source=Scripts/_burrow-secrets.sh
|
||||
source "${SCRIPT_DIR}/_burrow-secrets.sh"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
|
|
@ -12,17 +14,22 @@ Run a post-boot verification pass against the Burrow forge host.
|
|||
|
||||
Options:
|
||||
--host <user@host> SSH target (default: root@git.burrow.net)
|
||||
--ssh-key <path> SSH private key (default: intake/agent_at_burrow_net_ed25519)
|
||||
--ssh-key <path> SSH private key (default: secrets/forgejo/agent-ssh-key.age, then intake/)
|
||||
--expect-nsc Fail if forgejo-nsc services are not active
|
||||
-h, --help Show this help text
|
||||
EOF
|
||||
}
|
||||
|
||||
HOST="${BURROW_FORGE_HOST:-root@git.burrow.net}"
|
||||
SSH_KEY="${BURROW_FORGE_SSH_KEY:-${REPO_ROOT}/intake/agent_at_burrow_net_ed25519}"
|
||||
SSH_KEY="${BURROW_FORGE_SSH_KEY:-}"
|
||||
KNOWN_HOSTS_FILE="${BURROW_FORGE_KNOWN_HOSTS_FILE:-${HOME}/.cache/burrow/forge-known_hosts}"
|
||||
EXPECT_NSC=0
|
||||
|
||||
cleanup() {
|
||||
burrow_cleanup_secret_tmpfiles
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--host)
|
||||
|
|
@ -51,10 +58,17 @@ done
|
|||
|
||||
mkdir -p "$(dirname "${KNOWN_HOSTS_FILE}")"
|
||||
|
||||
if [[ ! -f "${SSH_KEY}" ]]; then
|
||||
echo "forge SSH key not found: ${SSH_KEY}" >&2
|
||||
SSH_KEY="$(
|
||||
burrow_resolve_secret_file \
|
||||
"${REPO_ROOT}" \
|
||||
"${SSH_KEY}" \
|
||||
"${REPO_ROOT}/intake/agent_at_burrow_net_ed25519" \
|
||||
"${REPO_ROOT}/secrets/forgejo/agent-ssh-key.age" \
|
||||
"${HOME}/.ssh/agent_at_burrow_net_ed25519"
|
||||
)" || {
|
||||
echo "forge SSH key could not be resolved" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
ssh \
|
||||
-i "${SSH_KEY}" \
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
# shellcheck source=Scripts/_burrow-secrets.sh
|
||||
source "${SCRIPT_DIR}/_burrow-secrets.sh"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: Scripts/cloudflare-upsert-a-record.sh --zone <zone> --name <fqdn> --ipv4 <address> [options]
|
||||
|
|
@ -13,7 +18,7 @@ Options:
|
|||
--name <fqdn> Fully-qualified DNS record name
|
||||
--ipv4 <address> IPv4 address for the A record
|
||||
--token-file <path> Cloudflare API token file
|
||||
default: intake/cloudflare-token.txt
|
||||
default: secrets/cloudflare/api-token.age, then intake/cloudflare-token.txt
|
||||
--ttl <seconds|auto> Record TTL, or auto
|
||||
default: auto
|
||||
--proxied <true|false> Whether to proxy through Cloudflare
|
||||
|
|
@ -25,10 +30,15 @@ EOF
|
|||
ZONE_NAME=""
|
||||
RECORD_NAME=""
|
||||
IPV4=""
|
||||
TOKEN_FILE="intake/cloudflare-token.txt"
|
||||
TOKEN_FILE="${CLOUDFLARE_TOKEN_FILE:-}"
|
||||
TTL_VALUE="auto"
|
||||
PROXIED="false"
|
||||
|
||||
cleanup() {
|
||||
burrow_cleanup_secret_tmpfiles
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--zone)
|
||||
|
|
@ -71,11 +81,16 @@ if [[ -z "${ZONE_NAME}" || -z "${RECORD_NAME}" || -z "${IPV4}" ]]; then
|
|||
usage >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [[ ! -f "${TOKEN_FILE}" ]]; then
|
||||
echo "Cloudflare token file not found: ${TOKEN_FILE}" >&2
|
||||
TOKEN_FILE="$(
|
||||
burrow_resolve_secret_file \
|
||||
"${REPO_ROOT}" \
|
||||
"${TOKEN_FILE}" \
|
||||
"${REPO_ROOT}/intake/cloudflare-token.txt" \
|
||||
"${REPO_ROOT}/secrets/cloudflare/api-token.age"
|
||||
)" || {
|
||||
echo "Cloudflare token file could not be resolved" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ ! "${IPV4}" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
|
||||
echo "Invalid IPv4 address: ${IPV4}" >&2
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|||
|
||||
# shellcheck source=Scripts/_burrow-flake.sh
|
||||
source "${SCRIPT_DIR}/_burrow-flake.sh"
|
||||
# shellcheck source=Scripts/_burrow-secrets.sh
|
||||
source "${SCRIPT_DIR}/_burrow-secrets.sh"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
|
|
@ -18,7 +20,7 @@ Defaults:
|
|||
|
||||
Environment:
|
||||
BURROW_FORGE_HOST root@git.burrow.net
|
||||
BURROW_FORGE_SSH_KEY intake/agent_at_burrow_net_ed25519
|
||||
BURROW_FORGE_SSH_KEY explicit path, otherwise secrets/forgejo/agent-ssh-key.age
|
||||
EOF
|
||||
}
|
||||
|
||||
|
|
@ -28,6 +30,7 @@ ALLOW_DIRTY=0
|
|||
BURROW_FLAKE_TMPDIRS=()
|
||||
|
||||
cleanup() {
|
||||
burrow_cleanup_secret_tmpfiles
|
||||
burrow_cleanup_flake_tmpdirs
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
|
@ -71,21 +74,17 @@ if [[ ${ALLOW_DIRTY} -ne 1 ]] && [[ -n "$(git status --short)" ]]; then
|
|||
fi
|
||||
|
||||
FORGE_HOST="${BURROW_FORGE_HOST:-root@git.burrow.net}"
|
||||
FORGE_SSH_KEY="${BURROW_FORGE_SSH_KEY:-}"
|
||||
|
||||
if [[ -z "${FORGE_SSH_KEY}" ]]; then
|
||||
if [[ -f "${REPO_ROOT}/intake/agent_at_burrow_net_ed25519" ]]; then
|
||||
FORGE_SSH_KEY="${REPO_ROOT}/intake/agent_at_burrow_net_ed25519"
|
||||
else
|
||||
FORGE_SSH_KEY="${HOME}/.ssh/agent_at_burrow_net_ed25519"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ ! -f "${FORGE_SSH_KEY}" ]]; then
|
||||
echo "Forge SSH key not found at ${FORGE_SSH_KEY}." >&2
|
||||
echo "Set BURROW_FORGE_SSH_KEY or place the agent key in intake/." >&2
|
||||
FORGE_SSH_KEY="$(
|
||||
burrow_resolve_secret_file \
|
||||
"${REPO_ROOT}" \
|
||||
"${BURROW_FORGE_SSH_KEY:-}" \
|
||||
"${REPO_ROOT}/intake/agent_at_burrow_net_ed25519" \
|
||||
"${REPO_ROOT}/secrets/forgejo/agent-ssh-key.age" \
|
||||
"${HOME}/.ssh/agent_at_burrow_net_ed25519"
|
||||
)" || {
|
||||
echo "Unable to resolve the forge SSH key." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
FORGE_KNOWN_HOSTS_FILE="${BURROW_FORGE_KNOWN_HOSTS_FILE:-${HOME}/.cache/burrow/forge-known_hosts}"
|
||||
mkdir -p "$(dirname "${FORGE_KNOWN_HOSTS_FILE}")"
|
||||
|
|
|
|||
|
|
@ -6,12 +6,14 @@ REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|||
|
||||
# shellcheck source=Scripts/_burrow-flake.sh
|
||||
source "${SCRIPT_DIR}/_burrow-flake.sh"
|
||||
# shellcheck source=Scripts/_burrow-secrets.sh
|
||||
source "${SCRIPT_DIR}/_burrow-secrets.sh"
|
||||
|
||||
DEFAULT_CONFIG="burrow-forge"
|
||||
DEFAULT_FLAKE="."
|
||||
DEFAULT_LOCATION="hel1"
|
||||
DEFAULT_ARCHITECTURE="x86"
|
||||
DEFAULT_TOKEN_FILE="${REPO_ROOT}/intake/hetzner-api-token.txt"
|
||||
DEFAULT_TOKEN_FILE=""
|
||||
|
||||
CONFIG="${HCLOUD_IMAGE_CONFIG:-${DEFAULT_CONFIG}}"
|
||||
FLAKE="${HCLOUD_IMAGE_FLAKE:-${DEFAULT_FLAKE}}"
|
||||
|
|
@ -30,6 +32,13 @@ NIX_BUILD_FLAGS=()
|
|||
BURROW_FLAKE_TMPDIRS=()
|
||||
LOCAL_STORE_DIR=""
|
||||
|
||||
cleanup() {
|
||||
burrow_cleanup_secret_tmpfiles
|
||||
burrow_cleanup_flake_tmpdirs
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: Scripts/hcloud-upload-nixos-image.sh [options]
|
||||
|
|
@ -42,7 +51,7 @@ Options:
|
|||
--location <code> Hetzner location for the temporary upload server (default: hel1)
|
||||
--architecture <x86|arm> CPU architecture of the image (default: x86)
|
||||
--server-type <name> Hetzner server type for the temporary upload server
|
||||
--token-file <path> Hetzner API token file (default: intake/hetzner-api-token.txt)
|
||||
--token-file <path> Hetzner API token file (default: secrets/hetzner/api-token.age, then intake/hetzner-api-token.txt)
|
||||
--artifact-path <path> Prebuilt raw image artifact to upload directly
|
||||
--output-hash <hash> Stable hash label for --artifact-path uploads
|
||||
--builder-spec <string> Complete builders string passed to nix build
|
||||
|
|
@ -125,6 +134,17 @@ while [[ $# -gt 0 ]]; do
|
|||
esac
|
||||
done
|
||||
|
||||
TOKEN_FILE="$(
|
||||
burrow_resolve_secret_file \
|
||||
"${REPO_ROOT}" \
|
||||
"${TOKEN_FILE}" \
|
||||
"${REPO_ROOT}/intake/hetzner-api-token.txt" \
|
||||
"${REPO_ROOT}/secrets/hetzner/api-token.age"
|
||||
)" || {
|
||||
echo "Hetzner API token file could not be resolved" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
burrow_cleanup_flake_tmpdirs
|
||||
if [[ -n "${LOCAL_STORE_DIR}" && -d "${LOCAL_STORE_DIR}" ]]; then
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@
|
|||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
# shellcheck source=Scripts/_burrow-secrets.sh
|
||||
source "${SCRIPT_DIR}/_burrow-secrets.sh"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
|
|
@ -31,7 +34,7 @@ Options:
|
|||
-h, --help Show this help text.
|
||||
|
||||
Environment:
|
||||
HCLOUD_TOKEN_FILE Defaults to intake/hetzner-api-token.txt
|
||||
HCLOUD_TOKEN_FILE Defaults to secrets/hetzner/api-token.age, then intake/hetzner-api-token.txt
|
||||
EOF
|
||||
}
|
||||
|
||||
|
|
@ -43,10 +46,15 @@ IMAGE="ubuntu-24.04"
|
|||
CONFIG="burrow-forge"
|
||||
FLAKE="."
|
||||
UPLOAD_LOCATION=""
|
||||
TOKEN_FILE="${HCLOUD_TOKEN_FILE:-intake/hetzner-api-token.txt}"
|
||||
TOKEN_FILE="${HCLOUD_TOKEN_FILE:-}"
|
||||
YES=0
|
||||
SSH_KEYS=("contact@burrow.net" "agent@burrow.net")
|
||||
|
||||
cleanup() {
|
||||
burrow_cleanup_secret_tmpfiles
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
if [[ $# -gt 0 ]]; then
|
||||
case "$1" in
|
||||
show|create|delete|recreate|build-image|create-from-image|recreate-from-image)
|
||||
|
|
@ -110,10 +118,16 @@ while [[ $# -gt 0 ]]; do
|
|||
esac
|
||||
done
|
||||
|
||||
if [[ ! -f "${TOKEN_FILE}" ]]; then
|
||||
echo "Hetzner API token file not found: ${TOKEN_FILE}" >&2
|
||||
TOKEN_FILE="$(
|
||||
burrow_resolve_secret_file \
|
||||
"${REPO_ROOT}" \
|
||||
"${TOKEN_FILE}" \
|
||||
"${REPO_ROOT}/intake/hetzner-api-token.txt" \
|
||||
"${REPO_ROOT}/secrets/hetzner/api-token.age"
|
||||
)" || {
|
||||
echo "Hetzner API token file could not be resolved" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ -z "${UPLOAD_LOCATION}" ]]; then
|
||||
UPLOAD_LOCATION="${LOCATION}"
|
||||
|
|
|
|||
|
|
@ -6,11 +6,13 @@ REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|||
|
||||
# shellcheck source=Scripts/_burrow-flake.sh
|
||||
source "${SCRIPT_DIR}/_burrow-flake.sh"
|
||||
# shellcheck source=Scripts/_burrow-secrets.sh
|
||||
source "${SCRIPT_DIR}/_burrow-secrets.sh"
|
||||
|
||||
CONFIG="${HCLOUD_IMAGE_CONFIG:-burrow-forge}"
|
||||
FLAKE="${HCLOUD_IMAGE_FLAKE:-.}"
|
||||
LOCATION="${HCLOUD_IMAGE_LOCATION:-hel1}"
|
||||
TOKEN_FILE="${HCLOUD_TOKEN_FILE:-${REPO_ROOT}/intake/hetzner-api-token.txt}"
|
||||
TOKEN_FILE="${HCLOUD_TOKEN_FILE:-}"
|
||||
NSC_SSH_HOST="${NSC_SSH_HOST:-ssh.ord2.namespace.so}"
|
||||
NSC_MACHINE_TYPE="${NSC_MACHINE_TYPE:-linux/amd64:32x64}"
|
||||
NSC_BUILDER_DURATION="${NSC_BUILDER_DURATION:-4h}"
|
||||
|
|
@ -26,6 +28,13 @@ EXTRA_LABELS=()
|
|||
BURROW_FLAKE_TMPDIRS=()
|
||||
BUILDER_ID=""
|
||||
|
||||
cleanup() {
|
||||
burrow_cleanup_secret_tmpfiles
|
||||
burrow_cleanup_flake_tmpdirs
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: Scripts/nsc-build-and-upload-image.sh [options]
|
||||
|
|
@ -37,7 +46,7 @@ Options:
|
|||
--config <name> images.<name>-raw output to build (default: burrow-forge)
|
||||
--flake <path> Flake path to build from (default: .)
|
||||
--location <code> Hetzner upload location (default: hel1)
|
||||
--token-file <path> Hetzner API token file (default: intake/hetzner-api-token.txt)
|
||||
--token-file <path> Hetzner API token file (default: secrets/hetzner/api-token.age, then intake/hetzner-api-token.txt)
|
||||
--machine-type <type> Namespace machine type (default: linux/amd64:32x64)
|
||||
--ssh-host <host> Namespace SSH endpoint (default: ssh.ord2.namespace.so)
|
||||
--duration <ttl> Namespace builder lifetime (default: 4h)
|
||||
|
|
@ -126,6 +135,17 @@ while [[ $# -gt 0 ]]; do
|
|||
esac
|
||||
done
|
||||
|
||||
TOKEN_FILE="$(
|
||||
burrow_resolve_secret_file \
|
||||
"${REPO_ROOT}" \
|
||||
"${TOKEN_FILE}" \
|
||||
"${REPO_ROOT}/intake/hetzner-api-token.txt" \
|
||||
"${REPO_ROOT}/secrets/hetzner/api-token.age"
|
||||
)" || {
|
||||
echo "Hetzner API token file could not be resolved" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
if [[ -n "${BUILDER_ID}" && -n "${NSC_BIN}" ]]; then
|
||||
"${NSC_BIN}" destroy "${BUILDER_ID}" --force >/dev/null 2>&1 || true
|
||||
|
|
|
|||
|
|
@ -6,31 +6,35 @@ REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|||
|
||||
# shellcheck source=Scripts/_burrow-flake.sh
|
||||
source "${SCRIPT_DIR}/_burrow-flake.sh"
|
||||
# shellcheck source=Scripts/_burrow-secrets.sh
|
||||
source "${SCRIPT_DIR}/_burrow-secrets.sh"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: Scripts/provision-forgejo-nsc.sh [options]
|
||||
|
||||
Generate Burrow forgejo-nsc runtime inputs in intake/ and optionally refresh the
|
||||
Namespace token from the currently logged-in namespace account.
|
||||
Generate Burrow forgejo-nsc runtime inputs and refresh the authoritative
|
||||
`secrets/forgejo/*.age` files, optionally refreshing the Namespace token from
|
||||
the currently logged-in namespace account.
|
||||
|
||||
Options:
|
||||
--host <user@host> SSH target used to mint the Forgejo PAT.
|
||||
Default: root@git.burrow.net
|
||||
--ssh-key <path> SSH private key for the forge host.
|
||||
Default: intake/agent_at_burrow_net_ed25519
|
||||
Default: secrets/forgejo/agent-ssh-key.age, then intake/
|
||||
--nsc-bin <path> Override the nsc binary.
|
||||
--no-refresh-token Reuse intake/forgejo_nsc_token.txt if it already exists.
|
||||
--no-refresh-token Reuse the existing encrypted Namespace token if it already exists.
|
||||
--token-name <name> Forgejo PAT name prefix (default: forgejo-nsc)
|
||||
--contact-user <name> Forgejo username used for PAT creation (default: contact)
|
||||
--scope-owner <name> Forgejo org/user owner for the default NSC scope (default: hackclub)
|
||||
--scope-name <name> Forgejo repository name for the default NSC scope (default: burrow)
|
||||
--write-intake Also write plaintext runtime inputs to intake/ for local debugging.
|
||||
-h, --help Show this help text.
|
||||
EOF
|
||||
}
|
||||
|
||||
HOST="${BURROW_FORGE_HOST:-root@git.burrow.net}"
|
||||
SSH_KEY="${BURROW_FORGE_SSH_KEY:-${REPO_ROOT}/intake/agent_at_burrow_net_ed25519}"
|
||||
SSH_KEY="${BURROW_FORGE_SSH_KEY:-}"
|
||||
NSC_BIN="${NSC_BIN:-}"
|
||||
KNOWN_HOSTS_FILE="${BURROW_FORGE_KNOWN_HOSTS_FILE:-${HOME}/.cache/burrow/forge-known_hosts}"
|
||||
REFRESH_TOKEN=1
|
||||
|
|
@ -39,8 +43,12 @@ CONTACT_USER="${FORGEJO_CONTACT_USER:-contact}"
|
|||
SCOPE_OWNER="${FORGEJO_SCOPE_OWNER:-hackclub}"
|
||||
SCOPE_NAME="${FORGEJO_SCOPE_NAME:-burrow}"
|
||||
BURROW_FLAKE_TMPDIRS=()
|
||||
WRITE_INTAKE=0
|
||||
TMP_DIR=""
|
||||
|
||||
cleanup() {
|
||||
[[ -n "${TMP_DIR}" ]] && rm -rf "${TMP_DIR}" >/dev/null 2>&1 || true
|
||||
burrow_cleanup_secret_tmpfiles
|
||||
burrow_cleanup_flake_tmpdirs
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
|
@ -79,6 +87,10 @@ while [[ $# -gt 0 ]]; do
|
|||
SCOPE_NAME="${2:?missing value for --scope-name}"
|
||||
shift 2
|
||||
;;
|
||||
--write-intake)
|
||||
WRITE_INTAKE=1
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
|
|
@ -97,13 +109,15 @@ burrow_require_cmd nix
|
|||
burrow_require_cmd ssh
|
||||
burrow_require_cmd python3
|
||||
|
||||
if [[ ! -f "${SSH_KEY}" ]]; then
|
||||
echo "forge SSH key not found: ${SSH_KEY}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "${REPO_ROOT}/intake"
|
||||
chmod 700 "${REPO_ROOT}/intake"
|
||||
SSH_KEY="$(
|
||||
burrow_resolve_secret_file \
|
||||
"${REPO_ROOT}" \
|
||||
"${SSH_KEY}" \
|
||||
"${REPO_ROOT}/intake/agent_at_burrow_net_ed25519" \
|
||||
"${REPO_ROOT}/secrets/forgejo/agent-ssh-key.age" \
|
||||
"${HOME}/.ssh/agent_at_burrow_net_ed25519"
|
||||
)"
|
||||
TMP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/burrow-forgejo-nsc.XXXXXX")"
|
||||
|
||||
flake_ref="$(burrow_prepare_flake_ref "${REPO_ROOT}")"
|
||||
if [[ -z "${NSC_BIN}" ]]; then
|
||||
|
|
@ -128,13 +142,16 @@ if [[ ! -x "${NSC_BIN}" ]]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
token_file="${REPO_ROOT}/intake/forgejo_nsc_token.txt"
|
||||
dispatcher_out="${REPO_ROOT}/intake/forgejo_nsc_dispatcher.yaml"
|
||||
autoscaler_out="${REPO_ROOT}/intake/forgejo_nsc_autoscaler.yaml"
|
||||
token_file="${TMP_DIR}/forgejo_nsc_token.txt"
|
||||
dispatcher_out="${TMP_DIR}/forgejo_nsc_dispatcher.yaml"
|
||||
autoscaler_out="${TMP_DIR}/forgejo_nsc_autoscaler.yaml"
|
||||
dispatcher_src="${REPO_ROOT}/services/forgejo-nsc/deploy/dispatcher.yaml"
|
||||
autoscaler_src="${REPO_ROOT}/services/forgejo-nsc/deploy/autoscaler.yaml"
|
||||
token_secret="${REPO_ROOT}/secrets/forgejo/nsc-token.age"
|
||||
dispatcher_secret="${REPO_ROOT}/secrets/forgejo/nsc-dispatcher-config.age"
|
||||
autoscaler_secret="${REPO_ROOT}/secrets/forgejo/nsc-autoscaler-config.age"
|
||||
|
||||
if [[ "${REFRESH_TOKEN}" -eq 1 || ! -s "${token_file}" ]]; then
|
||||
if [[ "${REFRESH_TOKEN}" -eq 1 ]]; then
|
||||
"${NSC_BIN}" auth check-login --duration 20m >/dev/null
|
||||
raw_token_file="$(mktemp)"
|
||||
trap 'rm -f "${raw_token_file}"; cleanup' EXIT
|
||||
|
|
@ -155,7 +172,13 @@ Path(os.environ["TOKEN_FILE"]).write_text(
|
|||
PY
|
||||
rm -f "${raw_token_file}"
|
||||
chmod 600 "${token_file}"
|
||||
elif [[ -s "${token_file}" ]]; then
|
||||
elif [[ -f "${token_secret}" ]]; then
|
||||
burrow_decrypt_age_secret_to_temp "${REPO_ROOT}" "${token_secret}" > "${token_file}"
|
||||
elif [[ -s "${REPO_ROOT}/intake/forgejo_nsc_token.txt" ]]; then
|
||||
cp "${REPO_ROOT}/intake/forgejo_nsc_token.txt" "${token_file}"
|
||||
fi
|
||||
|
||||
if [[ -s "${token_file}" ]]; then
|
||||
TOKEN_FILE="${token_file}" python3 - <<'PY'
|
||||
import json
|
||||
import os
|
||||
|
|
@ -271,6 +294,24 @@ PY
|
|||
|
||||
chmod 600 "${dispatcher_out}" "${autoscaler_out}"
|
||||
|
||||
echo "Rendered intake/forgejo_nsc_token.txt, intake/forgejo_nsc_dispatcher.yaml, and intake/forgejo_nsc_autoscaler.yaml."
|
||||
echo "Re-encrypt them into secrets/forgejo/{nsc-token,nsc-dispatcher-config,nsc-autoscaler-config}.age before deploying the forge host."
|
||||
burrow_encrypt_secret_from_file "${REPO_ROOT}" "${token_secret}" "${token_file}"
|
||||
burrow_encrypt_secret_from_file "${REPO_ROOT}" "${dispatcher_secret}" "${dispatcher_out}"
|
||||
burrow_encrypt_secret_from_file "${REPO_ROOT}" "${autoscaler_secret}" "${autoscaler_out}"
|
||||
|
||||
if [[ "${WRITE_INTAKE}" -eq 1 ]]; then
|
||||
mkdir -p "${REPO_ROOT}/intake"
|
||||
chmod 700 "${REPO_ROOT}/intake"
|
||||
cp "${token_file}" "${REPO_ROOT}/intake/forgejo_nsc_token.txt"
|
||||
cp "${dispatcher_out}" "${REPO_ROOT}/intake/forgejo_nsc_dispatcher.yaml"
|
||||
cp "${autoscaler_out}" "${REPO_ROOT}/intake/forgejo_nsc_autoscaler.yaml"
|
||||
chmod 600 \
|
||||
"${REPO_ROOT}/intake/forgejo_nsc_token.txt" \
|
||||
"${REPO_ROOT}/intake/forgejo_nsc_dispatcher.yaml" \
|
||||
"${REPO_ROOT}/intake/forgejo_nsc_autoscaler.yaml"
|
||||
fi
|
||||
|
||||
echo "Updated secrets/forgejo/{nsc-token,nsc-dispatcher-config,nsc-autoscaler-config}.age."
|
||||
if [[ "${WRITE_INTAKE}" -eq 1 ]]; then
|
||||
echo "Also refreshed intake/forgejo_nsc_{token,dispatcher,autoscaler} for local debugging."
|
||||
fi
|
||||
echo "Minted Forgejo PAT ${token_name} for ${CONTACT_USER} on ${HOST}."
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@ usage() {
|
|||
cat <<'EOF'
|
||||
Usage: Scripts/sync-forgejo-nsc-config.sh [options]
|
||||
|
||||
Copy Burrow forgejo-nsc runtime inputs from intake/ onto the forge host and
|
||||
Copy Burrow forgejo-nsc runtime inputs from age secrets or intake/ onto the forge host and
|
||||
restart the dispatcher/autoscaler units.
|
||||
|
||||
Options:
|
||||
--host <user@host> SSH target (default: root@git.burrow.net)
|
||||
--ssh-key <path> SSH private key (default: intake/agent_at_burrow_net_ed25519)
|
||||
--ssh-key <path> SSH private key (default: secrets/forgejo/agent-ssh-key.age, then intake/)
|
||||
--rotate-pat Re-render the intake files before syncing.
|
||||
--no-restart Copy files only.
|
||||
-h, --help Show this help text.
|
||||
|
|
@ -19,12 +19,21 @@ EOF
|
|||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
# shellcheck source=Scripts/_burrow-secrets.sh
|
||||
source "${SCRIPT_DIR}/_burrow-secrets.sh"
|
||||
|
||||
HOST="${BURROW_FORGE_HOST:-root@git.burrow.net}"
|
||||
SSH_KEY="${BURROW_FORGE_SSH_KEY:-${REPO_ROOT}/intake/agent_at_burrow_net_ed25519}"
|
||||
SSH_KEY="${BURROW_FORGE_SSH_KEY:-}"
|
||||
KNOWN_HOSTS_FILE="${BURROW_FORGE_KNOWN_HOSTS_FILE:-${HOME}/.cache/burrow/forge-known_hosts}"
|
||||
ROTATE_PAT=0
|
||||
NO_RESTART=0
|
||||
TMP_DIR=""
|
||||
|
||||
cleanup() {
|
||||
[[ -n "${TMP_DIR}" ]] && rm -rf "${TMP_DIR}" >/dev/null 2>&1 || true
|
||||
burrow_cleanup_secret_tmpfiles
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
|
|
@ -68,18 +77,41 @@ burrow_require_cmd() {
|
|||
burrow_require_cmd ssh
|
||||
burrow_require_cmd scp
|
||||
|
||||
if [[ ! -f "${SSH_KEY}" ]]; then
|
||||
echo "forge SSH key not found: ${SSH_KEY}" >&2
|
||||
exit 1
|
||||
fi
|
||||
SSH_KEY="$(
|
||||
burrow_resolve_secret_file \
|
||||
"${REPO_ROOT}" \
|
||||
"${SSH_KEY}" \
|
||||
"${REPO_ROOT}/intake/agent_at_burrow_net_ed25519" \
|
||||
"${REPO_ROOT}/secrets/forgejo/agent-ssh-key.age" \
|
||||
"${HOME}/.ssh/agent_at_burrow_net_ed25519"
|
||||
)"
|
||||
|
||||
if [[ "${ROTATE_PAT}" -eq 1 ]]; then
|
||||
"${SCRIPT_DIR}/provision-forgejo-nsc.sh" --host "${HOST}" --ssh-key "${SSH_KEY}"
|
||||
fi
|
||||
|
||||
token_file="${REPO_ROOT}/intake/forgejo_nsc_token.txt"
|
||||
dispatcher_file="${REPO_ROOT}/intake/forgejo_nsc_dispatcher.yaml"
|
||||
autoscaler_file="${REPO_ROOT}/intake/forgejo_nsc_autoscaler.yaml"
|
||||
TMP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/burrow-nsc-sync.XXXXXX")"
|
||||
token_file="$(
|
||||
burrow_resolve_secret_file \
|
||||
"${REPO_ROOT}" \
|
||||
"" \
|
||||
"${REPO_ROOT}/intake/forgejo_nsc_token.txt" \
|
||||
"${REPO_ROOT}/secrets/forgejo/nsc-token.age"
|
||||
)"
|
||||
dispatcher_file="$(
|
||||
burrow_resolve_secret_file \
|
||||
"${REPO_ROOT}" \
|
||||
"" \
|
||||
"${REPO_ROOT}/intake/forgejo_nsc_dispatcher.yaml" \
|
||||
"${REPO_ROOT}/secrets/forgejo/nsc-dispatcher-config.age"
|
||||
)"
|
||||
autoscaler_file="$(
|
||||
burrow_resolve_secret_file \
|
||||
"${REPO_ROOT}" \
|
||||
"" \
|
||||
"${REPO_ROOT}/intake/forgejo_nsc_autoscaler.yaml" \
|
||||
"${REPO_ROOT}/secrets/forgejo/nsc-autoscaler-config.age"
|
||||
)"
|
||||
|
||||
for path in "${token_file}" "${dispatcher_file}" "${autoscaler_file}"; do
|
||||
if [[ ! -s "${path}" ]]; then
|
||||
|
|
@ -96,12 +128,12 @@ ssh_opts=(
|
|||
)
|
||||
|
||||
remote_tmp="$(ssh "${ssh_opts[@]}" "${HOST}" "mktemp -d")"
|
||||
cleanup() {
|
||||
cleanup_remote() {
|
||||
if [[ -n "${remote_tmp:-}" ]]; then
|
||||
ssh "${ssh_opts[@]}" "${HOST}" "rm -rf '${remote_tmp}'" >/dev/null 2>&1 || true
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT
|
||||
trap 'cleanup_remote; cleanup' EXIT
|
||||
|
||||
scp "${ssh_opts[@]}" \
|
||||
"${token_file}" \
|
||||
|
|
|
|||
|
|
@ -3,17 +3,22 @@
|
|||
set -euo pipefail
|
||||
umask 077
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
# shellcheck source=Scripts/_burrow-secrets.sh
|
||||
source "${REPO_ROOT}/Scripts/_burrow-secrets.sh"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage:
|
||||
Tools/forwardemail-custom-s3.sh \
|
||||
--domain burrow.net \
|
||||
--api-token-file intake/forwardemail_api_token.txt \
|
||||
--api-token-file secrets/forwardemail/api-token.age \
|
||||
--s3-endpoint https://<endpoint> \
|
||||
--s3-region <region> \
|
||||
--s3-bucket <bucket> \
|
||||
--s3-access-key-file intake/hetzner-s3-user.txt \
|
||||
--s3-secret-key-file intake/hetzner-s3-secret.txt
|
||||
--s3-access-key-file secrets/forwardemail/hetzner-s3-user.age \
|
||||
--s3-secret-key-file secrets/forwardemail/hetzner-s3-secret.age
|
||||
|
||||
Options:
|
||||
--domain <domain> Forward Email domain to update.
|
||||
|
|
@ -54,13 +59,18 @@ read_secret() {
|
|||
printf '%s' "$value"
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
burrow_cleanup_secret_tmpfiles
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
domain=""
|
||||
api_token_file=""
|
||||
api_token_file="${FORWARDEMAIL_API_TOKEN_FILE:-}"
|
||||
s3_endpoint=""
|
||||
s3_region=""
|
||||
s3_bucket=""
|
||||
s3_access_key_file=""
|
||||
s3_secret_key_file=""
|
||||
s3_access_key_file="${FORWARDEMAIL_S3_ACCESS_KEY_FILE:-}"
|
||||
s3_secret_key_file="${FORWARDEMAIL_S3_SECRET_KEY_FILE:-}"
|
||||
test_only=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
|
|
@ -108,16 +118,38 @@ while [[ $# -gt 0 ]]; do
|
|||
done
|
||||
|
||||
[[ -n "$domain" ]] || fail "--domain is required"
|
||||
[[ -n "$api_token_file" ]] || fail "--api-token-file is required"
|
||||
[[ -n "$s3_endpoint" || "$test_only" == true ]] || fail "--s3-endpoint is required unless --test-only is set"
|
||||
[[ -n "$s3_region" || "$test_only" == true ]] || fail "--s3-region is required unless --test-only is set"
|
||||
[[ -n "$s3_bucket" || "$test_only" == true ]] || fail "--s3-bucket is required unless --test-only is set"
|
||||
[[ -n "$s3_access_key_file" || "$test_only" == true ]] || fail "--s3-access-key-file is required unless --test-only is set"
|
||||
[[ -n "$s3_secret_key_file" || "$test_only" == true ]] || fail "--s3-secret-key-file is required unless --test-only is set"
|
||||
|
||||
api_token_file="$(
|
||||
burrow_resolve_secret_file \
|
||||
"${REPO_ROOT}" \
|
||||
"${api_token_file}" \
|
||||
"${REPO_ROOT}/intake/forwardemail_api_token.txt" \
|
||||
"${REPO_ROOT}/secrets/forwardemail/api-token.age"
|
||||
)" || fail "unable to resolve Forward Email API token file"
|
||||
require_file "$api_token_file"
|
||||
api_token="$(read_secret "$api_token_file")"
|
||||
|
||||
if [[ "$test_only" != true ]]; then
|
||||
s3_access_key_file="$(
|
||||
burrow_resolve_secret_file \
|
||||
"${REPO_ROOT}" \
|
||||
"${s3_access_key_file}" \
|
||||
"${REPO_ROOT}/intake/hetzner-s3-user.txt" \
|
||||
"${REPO_ROOT}/secrets/forwardemail/hetzner-s3-user.age"
|
||||
)" || fail "unable to resolve Hetzner S3 access key file"
|
||||
s3_secret_key_file="$(
|
||||
burrow_resolve_secret_file \
|
||||
"${REPO_ROOT}" \
|
||||
"${s3_secret_key_file}" \
|
||||
"${REPO_ROOT}/intake/hetzner-s3-secret.txt" \
|
||||
"${REPO_ROOT}/secrets/forwardemail/hetzner-s3-secret.age"
|
||||
)" || fail "unable to resolve Hetzner S3 secret key file"
|
||||
require_file "$s3_access_key_file"
|
||||
require_file "$s3_secret_key_file"
|
||||
fi
|
||||
|
||||
if [[ "$test_only" == false ]]; then
|
||||
require_file "$s3_access_key_file"
|
||||
require_file "$s3_secret_key_file"
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import argparse
|
|||
import datetime as dt
|
||||
import hashlib
|
||||
import hmac
|
||||
import subprocess
|
||||
import sys
|
||||
import textwrap
|
||||
from pathlib import Path
|
||||
|
|
@ -13,11 +14,38 @@ from urllib.parse import urlencode, urlparse
|
|||
|
||||
import requests
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
|
||||
|
||||
def default_secret_path(age_rel: str, intake_rel: str) -> str:
|
||||
age_path = REPO_ROOT / age_rel
|
||||
if age_path.exists():
|
||||
return str(age_path)
|
||||
return intake_rel
|
||||
|
||||
|
||||
def read_secret(path: str) -> str:
|
||||
value = Path(path).read_text(encoding="utf-8").strip()
|
||||
file_path = Path(path)
|
||||
if not file_path.is_absolute():
|
||||
file_path = REPO_ROOT / file_path
|
||||
if file_path.suffix == ".age":
|
||||
value = subprocess.check_output(
|
||||
[
|
||||
"nix",
|
||||
"--extra-experimental-features",
|
||||
"nix-command flakes",
|
||||
"run",
|
||||
f"{REPO_ROOT}#agenix",
|
||||
"--",
|
||||
"-d",
|
||||
str(file_path),
|
||||
],
|
||||
text=True,
|
||||
).strip()
|
||||
else:
|
||||
value = file_path.read_text(encoding="utf-8").strip()
|
||||
if not value:
|
||||
raise SystemExit(f"error: empty secret file: {path}")
|
||||
raise SystemExit(f"error: empty secret file: {file_path}")
|
||||
return value
|
||||
|
||||
|
||||
|
|
@ -212,12 +240,12 @@ def parse_args() -> argparse.Namespace:
|
|||
parser.add_argument("--region", default="hel1", help="S3 region.")
|
||||
parser.add_argument(
|
||||
"--access-key-file",
|
||||
default="intake/hetzner-s3-user.txt",
|
||||
default=default_secret_path("secrets/forwardemail/hetzner-s3-user.age", "intake/hetzner-s3-user.txt"),
|
||||
help="File containing the S3 access key id.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--secret-key-file",
|
||||
default="intake/hetzner-s3-secret.txt",
|
||||
default=default_secret_path("secrets/forwardemail/hetzner-s3-secret.age", "intake/hetzner-s3-secret.txt"),
|
||||
help="File containing the S3 secret key.",
|
||||
)
|
||||
parser.add_argument(
|
||||
|
|
|
|||
|
|
@ -26,11 +26,14 @@ Forward Email also documents these operational constraints:
|
|||
|
||||
## Burrow Secret Layout
|
||||
|
||||
Present in `intake/` today:
|
||||
Authoritative secrets now live in:
|
||||
|
||||
- `intake/forwardemail_api_token.txt`
|
||||
- `intake/hetzner-s3-user.txt`
|
||||
- `intake/hetzner-s3-secret.txt`
|
||||
- `secrets/forwardemail/api-token.age`
|
||||
- `secrets/forwardemail/hetzner-s3-user.age`
|
||||
- `secrets/forwardemail/hetzner-s3-secret.age`
|
||||
|
||||
Legacy plaintext `intake/` files may still exist locally for debugging, but the
|
||||
tooling now prefers the age-encrypted files above.
|
||||
- Hetzner public S3 endpoint for Forward Email: `https://hel1.your-objectstorage.com`
|
||||
- Hetzner object storage region: `hel1`
|
||||
- Hetzner bucket used for Forward Email backups: `burrow`
|
||||
|
|
@ -69,12 +72,12 @@ Example:
|
|||
```sh
|
||||
Tools/forwardemail-custom-s3.sh \
|
||||
--domain burrow.net \
|
||||
--api-token-file intake/forwardemail_api_token.txt \
|
||||
--api-token-file secrets/forwardemail/api-token.age \
|
||||
--s3-endpoint https://hel1.your-objectstorage.com \
|
||||
--s3-region hel1 \
|
||||
--s3-bucket burrow \
|
||||
--s3-access-key-file intake/hetzner-s3-user.txt \
|
||||
--s3-secret-key-file intake/hetzner-s3-secret.txt
|
||||
--s3-access-key-file secrets/forwardemail/hetzner-s3-user.age \
|
||||
--s3-secret-key-file secrets/forwardemail/hetzner-s3-secret.age
|
||||
```
|
||||
|
||||
Retest an existing domain configuration without rewriting it:
|
||||
|
|
@ -82,7 +85,7 @@ Retest an existing domain configuration without rewriting it:
|
|||
```sh
|
||||
Tools/forwardemail-custom-s3.sh \
|
||||
--domain burrow.net \
|
||||
--api-token-file intake/forwardemail_api_token.txt \
|
||||
--api-token-file secrets/forwardemail/api-token.age \
|
||||
--test-only
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ Mail hosting is intentionally not part of this NixOS host in the current plan. B
|
|||
3. Encrypt the Forgejo admin password and agent SSH key into `secrets/forgejo/{admin-password,agent-ssh-key}.age`.
|
||||
4. Let `burrow-forgejo-bootstrap.service` create or rotate the initial Forgejo admin account from the agenix secret path.
|
||||
5. Let `burrow-forgejo-runner-bootstrap.service` register the self-hosted Forgejo runner and seed Git identity as `agent <agent@burrow.net>`.
|
||||
6. Run `Scripts/provision-forgejo-nsc.sh` locally, re-encrypt the resulting NSC token + configs into `secrets/forgejo/*.age`, then deploy with `Scripts/forge-deploy.sh` so agenix updates the live forgejo-nsc runtime paths.
|
||||
6. Run `Scripts/provision-forgejo-nsc.sh` locally to refresh `secrets/forgejo/{nsc-token,nsc-dispatcher-config,nsc-autoscaler-config}.age`, then deploy with `Scripts/forge-deploy.sh` so agenix updates the live forgejo-nsc runtime paths.
|
||||
7. Use `Scripts/cloudflare-upsert-a-record.sh` to point `git.burrow.net`, `burrow.net`, and `nsc-autoscaler.burrow.net` at the host with Cloudflare proxying disabled for ACME.
|
||||
8. Use `Scripts/forge-deploy.sh --allow-dirty` for subsequent remote `nixos-rebuild` runs from the live workspace.
|
||||
9. Configure Forward Email custom S3 backups for `burrow.net` and `burrow.rs` out-of-band with `Tools/forwardemail-custom-s3.sh`.
|
||||
|
|
@ -43,7 +43,7 @@ Mail hosting is intentionally not part of this NixOS host in the current plan. B
|
|||
- `https://burrow.net` returns the root forge landing response
|
||||
- `https://git.burrow.net` returns the live Forgejo front door
|
||||
- `https://nsc-autoscaler.burrow.net` terminates TLS on Caddy and returns the expected application-level `404` for `/`
|
||||
- The Cloudflare token currently in `intake/cloudflare-token.txt` is an account-scoped token: `POST /accounts/<account>/tokens/verify` succeeds, while `POST /user/tokens/verify` returns `Invalid API Token`.
|
||||
- The Cloudflare token now lives in `secrets/cloudflare/api-token.age`; the current token is account-scoped: `POST /accounts/<account>/tokens/verify` succeeds, while `POST /user/tokens/verify` returns `Invalid API Token`.
|
||||
- `burrow.rs` still resolves publicly to a Vercel `DEPLOYMENT_NOT_FOUND` response.
|
||||
- Both domains publish Forward Email MX/TXT records.
|
||||
- Forward Email custom S3 is live on both domains against the Hetzner `burrow` bucket and the public regional endpoint `https://hel1.your-objectstorage.com`.
|
||||
|
|
|
|||
|
|
@ -9,11 +9,19 @@ For the Forgejo Namespace Cloud runtime:
|
|||
- `secrets/forgejo/nsc-token.age`
|
||||
- `secrets/forgejo/nsc-dispatcher-config.age`
|
||||
- `secrets/forgejo/nsc-autoscaler-config.age`
|
||||
- `secrets/cloudflare/api-token.age`
|
||||
- `secrets/hetzner/api-token.age`
|
||||
- `secrets/forwardemail/api-token.age`
|
||||
- `secrets/forwardemail/hetzner-s3-user.age`
|
||||
- `secrets/forwardemail/hetzner-s3-secret.age`
|
||||
|
||||
Use:
|
||||
|
||||
- `make secret name=forgejo/nsc-token`
|
||||
- `make secret-file name=forgejo/agent-ssh-key file=/path/to/source`
|
||||
- `Scripts/provision-forgejo-nsc.sh` to refresh the Forgejo Namespace token and runtime configs in `secrets/forgejo/*.age`
|
||||
- `make secret-file name=cloudflare/api-token file=/path/to/cloudflare-token.txt`
|
||||
- `make secret-file name=hetzner/api-token file=/path/to/hetzner-api-token.txt`
|
||||
|
||||
The forge host decrypts these files at activation time and feeds the resulting
|
||||
paths into `services.burrow.forge`, `services.burrow.forgeRunner`, and
|
||||
|
|
|
|||
7
secrets/cloudflare/api-token.age
Normal file
7
secrets/cloudflare/api-token.age
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
age-encryption.org/v1
|
||||
-> ssh-ed25519 ux4N8Q rX5+bmtxyHNgD+xNdHkB1fKdjUlrX275DaKTIHssYyA
|
||||
KwbfKHx14QXRKBIGWwJDR8+DONyCdVssh8Ti8mdajyQ
|
||||
-> ssh-ed25519 IrZmAg SOG/KvURA6PrxVhtZyIbazFGNQZyp0BR4MH+YInHGB4
|
||||
79pENXhtLwlCQVnqkPEzoFgrXMmTqRsfs4ULluTevWA
|
||||
--- gDA64KNbgN+eGHsQbIbKvhOg1T/Nqui6I/wy2MK8VWE
|
||||
û<EFBFBD>[|V{[ƒöŽ’ýö¯'E .Í{CÃǶÕö{ha
|
||||
7
secrets/forwardemail/api-token.age
Normal file
7
secrets/forwardemail/api-token.age
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
age-encryption.org/v1
|
||||
-> ssh-ed25519 ux4N8Q ICuXuDsZiw1ShfUX9qjq8bCkeNdsbHWnG4e+3ZOC3jg
|
||||
wswxqzQtf7jumSYB8ZeQzRBpMrBPVsUnWOYsmlDvpSs
|
||||
-> ssh-ed25519 IrZmAg Xrvp/tXzXrHF1+NxgTZs9nNufyxtTq5NoYT5gaW6p1M
|
||||
UWGlhZpV19CWMR9abp30vkQwZUMb/ylvInGEBlDdjjE
|
||||
--- qhAaAECwhmAY4g3/e+Dz9RvL1MBQkHGWyoe1NkdTuqA
|
||||
ìÑdÍéé?)¼ ñ<3ïŽ6ÜF:a•Ë<E280A2>
ųñÖ²Ä
|
||||
7
secrets/forwardemail/hetzner-s3-secret.age
Normal file
7
secrets/forwardemail/hetzner-s3-secret.age
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
age-encryption.org/v1
|
||||
-> ssh-ed25519 ux4N8Q jwJzvmXUV5rCB6ku7ILLQUDInuQJL2gN+pjmX/ccXWE
|
||||
q9OSyVhTuzERRRZZOCQzbwAwLOvOFIT/l9MxJ0V3UTo
|
||||
-> ssh-ed25519 IrZmAg 8IutYG3CnNP9gw5fTFOaXm1Ue4i/cVs1apA88bNs9mo
|
||||
daaf+6HoE3bmUEKR8/zu9jKTstVFCXqBlBxBdNVpQ90
|
||||
--- gRGNkWqoh+lZWpDG7yNLd4fjoX2jCyHTWbzImzoFGko
|
||||
R@+‰fu9ËÏRB‘±áÎX³2öúæ<C3BA>“[I¤<49>®
|
||||
7
secrets/forwardemail/hetzner-s3-user.age
Normal file
7
secrets/forwardemail/hetzner-s3-user.age
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
age-encryption.org/v1
|
||||
-> ssh-ed25519 ux4N8Q jwyFpeVX18Q/1vnK2A1gwETTTH/QDUmW7vhCA+E/1lc
|
||||
vtG1Ra+hR0cc/o9oJw7YTWMc2+JmrehzBE5QkIHQMKY
|
||||
-> ssh-ed25519 IrZmAg KljcDNRlBmn7ElVfXq/E2prFHnRQD2TkQY9Vto+OQUA
|
||||
T37sFc3xVrhky6e0n4KbsX18/fBqP3VjS/mNbxX6bfI
|
||||
--- lvSjWGriUCYC14eI2eH9MdO2cB76Pe3gWD7pidw8Qjo
|
||||
s‘&¾Ùßxö™<C3B6>*‘°4–}‰<1D>Í”z&¢F¥Å
|
||||
7
secrets/hetzner/api-token.age
Normal file
7
secrets/hetzner/api-token.age
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
age-encryption.org/v1
|
||||
-> ssh-ed25519 ux4N8Q pEJA2VJkPC+NzA9yFvBrpXHD8qFMTD9iIHYSkx8P2RI
|
||||
AGE1QJya77d92ERA1yQYylvZPNAJEQKoCL32BY5XBzo
|
||||
-> ssh-ed25519 IrZmAg VMpoTBpNG/TAlnbJ2APwc4VMt2CX5rQwlrrihtmojFo
|
||||
caOwayLgVDGPrjqLLH8hHHQ3Fy2WeRI2tf+R02HFqx0
|
||||
--- Ey1DYpyA4lnVqPaabNsEuSihl4fvZ2vpSc/IRGZwYBw
|
||||
¥Uï2Q÷‘âÖã*ð÷m¹¼†<C2BC>F<EFBFBD>ÒÞž|^–EVÜ"
|
||||
|
|
@ -3,6 +3,7 @@ let
|
|||
agent = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEN0+tRJy7Y2DW0uGYHb86N2t02WyU5lDNX6FaxBF/G8 agent@burrow.net";
|
||||
forge = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAlkGo4lwpwIIZ0J01KjTuJuf/U/wGgy4/aKwPIUzutL root@burrow-forge";
|
||||
|
||||
operatorSecrets = [ contact agent ];
|
||||
forgeAutomation = [ contact agent forge ];
|
||||
in {
|
||||
"secrets/forgejo/admin-password.age".publicKeys = forgeAutomation;
|
||||
|
|
@ -10,4 +11,9 @@ in {
|
|||
"secrets/forgejo/nsc-token.age".publicKeys = forgeAutomation;
|
||||
"secrets/forgejo/nsc-dispatcher-config.age".publicKeys = forgeAutomation;
|
||||
"secrets/forgejo/nsc-autoscaler-config.age".publicKeys = forgeAutomation;
|
||||
"secrets/cloudflare/api-token.age".publicKeys = operatorSecrets;
|
||||
"secrets/hetzner/api-token.age".publicKeys = operatorSecrets;
|
||||
"secrets/forwardemail/api-token.age".publicKeys = operatorSecrets;
|
||||
"secrets/forwardemail/hetzner-s3-user.age".publicKeys = operatorSecrets;
|
||||
"secrets/forwardemail/hetzner-s3-secret.age".publicKeys = operatorSecrets;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -155,11 +155,12 @@ instances:
|
|||
```
|
||||
|
||||
For Burrow, use `Scripts/provision-forgejo-nsc.sh` to mint the Forgejo PAT,
|
||||
generate a Namespace token from the logged-in namespace account, and render
|
||||
bootstrap artifacts into `intake/forgejo_nsc_{dispatcher,autoscaler}.yaml` plus
|
||||
`intake/forgejo_nsc_token.txt`. The token file is emitted as JSON with a
|
||||
`bearer_token` field so both the Compute API path and the `nsc` CLI fallback can
|
||||
consume the same secret material.
|
||||
generate a Namespace token from the logged-in Namespace account, and refresh
|
||||
`secrets/forgejo/{nsc-token,nsc-dispatcher-config,nsc-autoscaler-config}.age`.
|
||||
The token file is emitted as JSON with a `bearer_token` field so both the
|
||||
Compute API path and the `nsc` CLI fallback can consume the same secret
|
||||
material. Use `--write-intake` only when you explicitly need local plaintext
|
||||
debug copies.
|
||||
|
||||
Long-lived runtime state is now sourced from age-encrypted files:
|
||||
|
||||
|
|
@ -169,10 +170,9 @@ Long-lived runtime state is now sourced from age-encrypted files:
|
|||
- `secrets/forgejo/nsc-dispatcher-config.age`
|
||||
- `secrets/forgejo/nsc-autoscaler-config.age`
|
||||
|
||||
After refreshing the intake files, re-encrypt them into `secrets/forgejo/*.age`
|
||||
and deploy the forge host so `config.age.secrets.*` updates the live paths for
|
||||
`services.burrow.forge`, `services.burrow.forgeRunner`, and
|
||||
`services.burrow.forgejoNsc`.
|
||||
After refreshing the encrypted secrets, deploy the forge host so
|
||||
`config.age.secrets.*` updates the live paths for `services.burrow.forge`,
|
||||
`services.burrow.forgeRunner`, and `services.burrow.forgejoNsc`.
|
||||
|
||||
Run it next to the dispatcher:
|
||||
|
||||
|
|
|
|||
|
|
@ -602,6 +602,18 @@ if ! mkdir -p "/Users/runner/.cache/act" 2>/dev/null; then
|
|||
fi
|
||||
|
||||
export PATH="/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin:/usr/sbin:/sbin:${PATH}"
|
||||
cache_root="${NSC_CACHE_PATH:-$HOME/.cache/burrow}"
|
||||
mkdir -p \
|
||||
"${cache_root}/bin" \
|
||||
"${cache_root}/downloads" \
|
||||
"${cache_root}/go/path" \
|
||||
"${cache_root}/go/mod" \
|
||||
"${cache_root}/go/build" \
|
||||
"${cache_root}/homebrew"
|
||||
export HOMEBREW_CACHE="${cache_root}/homebrew"
|
||||
export GOPATH="${cache_root}/go/path"
|
||||
export GOMODCACHE="${cache_root}/go/mod"
|
||||
export GOCACHE="${cache_root}/go/build"
|
||||
|
||||
if ! command -v curl >/dev/null 2>&1; then
|
||||
echo "curl is required" >&2
|
||||
|
|
@ -622,14 +634,18 @@ export PATH="${PWD}/bin:${PATH}"
|
|||
|
||||
runner_version="v12.6.4"
|
||||
runner_src_tgz="forgejo-runner-${runner_version}.tar.gz"
|
||||
runner_src_tgz_path="${cache_root}/downloads/${runner_src_tgz}"
|
||||
runner_src_url="https://code.forgejo.org/forgejo/runner/archive/${runner_version}.tar.gz"
|
||||
runner_src_dir="forgejo-runner-src"
|
||||
runner_bin_cache="${cache_root}/bin/forgejo-runner-${runner_version}"
|
||||
|
||||
if ! command -v forgejo-runner >/dev/null 2>&1; then
|
||||
if [[ ! -x "${runner_bin_cache}" ]]; then
|
||||
rm -rf "${runner_src_dir}"
|
||||
mkdir -p "${runner_src_dir}"
|
||||
curl -fsSL "${runner_src_url}" -o "${runner_src_tgz}"
|
||||
tar -xzf "${runner_src_tgz}" -C "${runner_src_dir}" --strip-components=1
|
||||
if [[ ! -f "${runner_src_tgz_path}" ]]; then
|
||||
curl -fsSL "${runner_src_url}" -o "${runner_src_tgz_path}"
|
||||
fi
|
||||
tar -xzf "${runner_src_tgz_path}" -C "${runner_src_dir}" --strip-components=1
|
||||
|
||||
toolchain="$(grep -E '^toolchain ' "${runner_src_dir}/go.mod" | awk '{print $2}' | head -n 1 || true)"
|
||||
if [ -z "${toolchain}" ]; then
|
||||
|
|
@ -639,21 +655,23 @@ if ! command -v forgejo-runner >/dev/null 2>&1; then
|
|||
if ! command -v go >/dev/null 2>&1; then
|
||||
go_tgz="${toolchain}.darwin-arm64.tar.gz"
|
||||
go_url="https://go.dev/dl/${go_tgz}"
|
||||
curl -fsSL "${go_url}" -o "${go_tgz}"
|
||||
tar -xzf "${go_tgz}"
|
||||
go_tgz_path="${cache_root}/downloads/${go_tgz}"
|
||||
if [[ ! -f "${go_tgz_path}" ]]; then
|
||||
curl -fsSL "${go_url}" -o "${go_tgz_path}"
|
||||
fi
|
||||
tar -xzf "${go_tgz_path}"
|
||||
export GOROOT="${PWD}/go"
|
||||
export PATH="${GOROOT}/bin:${PATH}"
|
||||
fi
|
||||
|
||||
export GOPATH="${PWD}/.gopath"
|
||||
export GOMODCACHE="${PWD}/.gomodcache"
|
||||
export GOCACHE="${PWD}/.gocache"
|
||||
mkdir -p "${GOPATH}" "${GOMODCACHE}" "${GOCACHE}"
|
||||
|
||||
(cd "${runner_src_dir}" && go build -o "${workdir}/bin/forgejo-runner" .)
|
||||
chmod +x "${workdir}/bin/forgejo-runner"
|
||||
(cd "${runner_src_dir}" && go build -o "${runner_bin_cache}" .)
|
||||
chmod +x "${runner_bin_cache}"
|
||||
fi
|
||||
|
||||
ln -sf "${runner_bin_cache}" "${workdir}/bin/forgejo-runner"
|
||||
|
||||
cat > runner.yaml <<'EOF'
|
||||
log:
|
||||
level: info
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue