Store all production secrets encrypted in secrets.age (committed to the repo) using age. Only one secret needs to be in CI: SECRETS_AGE_KEY. When a secret changes locally, update secrets.env and re-run scripts/secrets-encrypt.sh to commit a new secrets.age. CI picks up the updated secrets automatically on the next push — no manual CI variable updates required. Changes: - scripts/secrets-encrypt.sh: encrypt secrets.env → secrets.age - scripts/secrets-decrypt.sh: decrypt secrets.age → GITHUB_ENV (CI) or eval-safe export block (local) - scripts/test_secrets.sh: encrypt/decrypt round-trip tests - secrets.env.example: template documenting all production secret keys - ci/main.go: add CheckSecrets function (runs test_secrets.sh via Dagger), wire into Check(), update Graph(); add age to toolchain apt packages - .forgejo/Dockerfile: add age package - .forgejo/workflows/deploy.yml: replace per-secret CI references with a single "Decrypt production secrets" step using SECRETS_AGE_KEY - flake.nix: add age to dev shell - Taskfile.yml: add check-secrets task, include in check-fast - .gitignore: ignore plaintext secrets.env - DAGGER.md: document Option 5 (encrypted secrets file) as active approach Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
43 lines
1.6 KiB
Bash
Executable File
43 lines
1.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Encrypts secrets.env → secrets.age using an age public key.
|
|
#
|
|
# Usage:
|
|
# scripts/secrets-encrypt.sh [AGE1...] public key as positional argument
|
|
# AGE_PUBLIC_KEY=AGE1... scripts/secrets-encrypt.sh
|
|
# scripts/secrets-encrypt.sh reads public key from .age-public-key
|
|
#
|
|
# The private key never touches this script. Only the public key is needed to
|
|
# encrypt. Store the private key in CI as SECRETS_AGE_KEY and keep a local
|
|
# copy at ~/.config/age/sharedinbox.key (or wherever you prefer).
|
|
set -euo pipefail
|
|
|
|
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null) \
|
|
|| REPO_ROOT=$(cd "$(dirname "$0")/.." && pwd)
|
|
SECRETS_ENV="${SECRETS_ENV:-${REPO_ROOT}/secrets.env}"
|
|
SECRETS_AGE="${SECRETS_AGE:-${REPO_ROOT}/secrets.age}"
|
|
KEY_FILE="${REPO_ROOT}/.age-public-key"
|
|
|
|
if [ -n "${1:-}" ]; then
|
|
PUBLIC_KEY="$1"
|
|
elif [ -n "${AGE_PUBLIC_KEY:-}" ]; then
|
|
PUBLIC_KEY="$AGE_PUBLIC_KEY"
|
|
elif [ -f "$KEY_FILE" ]; then
|
|
PUBLIC_KEY=$(cat "$KEY_FILE")
|
|
PUBLIC_KEY="${PUBLIC_KEY%%$'\n'*}" # take only the first line
|
|
else
|
|
echo "ERROR: No age public key provided." >&2
|
|
echo " Pass it as an argument: scripts/secrets-encrypt.sh AGE1..." >&2
|
|
echo " Or store it in .age-public-key: age-keygen -y ~/.config/age/sharedinbox.key > .age-public-key" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [ ! -f "$SECRETS_ENV" ]; then
|
|
echo "ERROR: secrets.env not found at $SECRETS_ENV" >&2
|
|
echo " Copy secrets.env.example to secrets.env and fill in values." >&2
|
|
exit 1
|
|
fi
|
|
|
|
age --encrypt --recipient "$PUBLIC_KEY" --output "$SECRETS_AGE" "$SECRETS_ENV"
|
|
echo "Encrypted $SECRETS_ENV → $SECRETS_AGE"
|
|
echo "Commit secrets.age to keep CI in sync."
|