refactor: split supervisor into infra + per-project, make poll scripts config-driven
Supervisor split (#26): - Layer 1 (infra): P0 memory, P1 disk, P4 housekeeping — runs once, project-agnostic - Layer 2 (per-project): P2 CI/dev-agent, P3 PRs/deps — iterates projects/*.toml - Adding a new project requires only a new TOML file, no code changes Poll scripts accept project TOML arg (#27): - dev-poll.sh, review-poll.sh, gardener-poll.sh accept optional project TOML as $1 - env.sh loads PROJECT_TOML if set, overriding .env defaults - Cron: `dev-poll.sh projects/versi.toml` targets that project New files: - lib/load-project.sh: TOML to env var loader (Python tomllib) - projects/versi.toml: current project config extracted from .env Backwards compatible: scripts without a TOML arg fall back to .env config. Closes #26, Closes #27 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e503273fba
commit
9050413994
7 changed files with 438 additions and 297 deletions
|
|
@ -19,6 +19,11 @@ fi
|
|||
export PATH="${HOME}/.local/bin:${HOME}/.foundry/bin:${HOME}/.nvm/versions/node/v22.20.0/bin:/usr/local/bin:/usr/bin:/bin:${PATH}"
|
||||
export HOME="${HOME:-/home/debian}"
|
||||
|
||||
# Load project TOML if PROJECT_TOML is set (by poll scripts that accept project arg)
|
||||
if [ -n "${PROJECT_TOML:-}" ] && [ -f "$PROJECT_TOML" ]; then
|
||||
source "${FACTORY_ROOT}/lib/load-project.sh" "$PROJECT_TOML"
|
||||
fi
|
||||
|
||||
# Codeberg token: env var > ~/.netrc
|
||||
if [ -z "${CODEBERG_TOKEN:-}" ]; then
|
||||
CODEBERG_TOKEN="$(awk '/codeberg.org/{getline;getline;print $2}' ~/.netrc 2>/dev/null || true)"
|
||||
|
|
|
|||
83
lib/load-project.sh
Executable file
83
lib/load-project.sh
Executable file
|
|
@ -0,0 +1,83 @@
|
|||
#!/usr/bin/env bash
|
||||
# load-project.sh — Load project config from a TOML file into env vars
|
||||
#
|
||||
# Usage (source, don't execute):
|
||||
# source lib/load-project.sh projects/harb.toml
|
||||
#
|
||||
# Exports:
|
||||
# PROJECT_NAME, CODEBERG_REPO, CODEBERG_API, PROJECT_REPO_ROOT,
|
||||
# PRIMARY_BRANCH, WOODPECKER_REPO_ID, PROJECT_CONTAINERS,
|
||||
# CHECK_PRS, CHECK_DEV_AGENT, CHECK_PIPELINE_STALL, CI_STALE_MINUTES
|
||||
#
|
||||
# If no argument given, does nothing (allows poll scripts to work with
|
||||
# plain .env fallback for backwards compatibility).
|
||||
|
||||
_PROJECT_TOML="${1:-}"
|
||||
|
||||
if [ -z "$_PROJECT_TOML" ] || [ ! -f "$_PROJECT_TOML" ]; then
|
||||
return 0 2>/dev/null || exit 0
|
||||
fi
|
||||
|
||||
# Parse TOML to shell variable assignments via Python
|
||||
_PROJECT_VARS=$(python3 -c "
|
||||
import sys, tomllib
|
||||
|
||||
with open(sys.argv[1], 'rb') as f:
|
||||
cfg = tomllib.load(f)
|
||||
|
||||
def emit(key, val):
|
||||
if isinstance(val, bool):
|
||||
print(f'{key}={str(val).lower()}')
|
||||
elif isinstance(val, list):
|
||||
print(f'{key}={\" \".join(str(v) for v in val)}')
|
||||
else:
|
||||
print(f'{key}={val}')
|
||||
|
||||
# Top-level
|
||||
emit('PROJECT_NAME', cfg.get('name', ''))
|
||||
emit('CODEBERG_REPO', cfg.get('repo', ''))
|
||||
|
||||
if 'repo_root' in cfg:
|
||||
emit('PROJECT_REPO_ROOT', cfg['repo_root'])
|
||||
if 'primary_branch' in cfg:
|
||||
emit('PRIMARY_BRANCH', cfg['primary_branch'])
|
||||
|
||||
# [ci] section
|
||||
ci = cfg.get('ci', {})
|
||||
if 'woodpecker_repo_id' in ci:
|
||||
emit('WOODPECKER_REPO_ID', ci['woodpecker_repo_id'])
|
||||
if 'stale_minutes' in ci:
|
||||
emit('CI_STALE_MINUTES', ci['stale_minutes'])
|
||||
|
||||
# [services] section
|
||||
svc = cfg.get('services', {})
|
||||
if 'containers' in svc:
|
||||
emit('PROJECT_CONTAINERS', svc['containers'])
|
||||
|
||||
# [monitoring] section
|
||||
mon = cfg.get('monitoring', {})
|
||||
for key in ['check_prs', 'check_dev_agent', 'check_pipeline_stall']:
|
||||
if key in mon:
|
||||
emit(key.upper(), mon[key])
|
||||
" "$_PROJECT_TOML" 2>/dev/null) || {
|
||||
echo "WARNING: failed to parse project TOML: $_PROJECT_TOML" >&2
|
||||
return 1 2>/dev/null || exit 1
|
||||
}
|
||||
|
||||
# Export parsed variables
|
||||
while IFS='=' read -r _key _val; do
|
||||
[ -z "$_key" ] && continue
|
||||
export "$_key=$_val"
|
||||
done <<< "$_PROJECT_VARS"
|
||||
|
||||
# Derive CODEBERG_API if repo changed
|
||||
if [ -n "$CODEBERG_REPO" ]; then
|
||||
export CODEBERG_API="https://codeberg.org/api/v1/repos/${CODEBERG_REPO}"
|
||||
fi
|
||||
|
||||
# Derive PROJECT_REPO_ROOT if not explicitly set
|
||||
if [ -z "${PROJECT_REPO_ROOT:-}" ] && [ -n "${PROJECT_NAME:-}" ]; then
|
||||
export PROJECT_REPO_ROOT="/home/${USER}/${PROJECT_NAME}"
|
||||
fi
|
||||
|
||||
unset _PROJECT_TOML _PROJECT_VARS _key _val
|
||||
Loading…
Add table
Add a link
Reference in a new issue