fix: Replace Codeberg dependency with local Forgejo instance (#611)
- Add setup_forge() to bin/disinto: provisions Forgejo via Docker, creates admin + bot users (dev-bot, review-bot), generates API tokens, creates repo, and pushes code — all automated - Rename env vars: CODEBERG_TOKEN→FORGE_TOKEN, REVIEW_BOT_TOKEN→ FORGE_REVIEW_TOKEN, CODEBERG_REPO→FORGE_REPO, CODEBERG_API→ FORGE_API, CODEBERG_WEB→FORGE_WEB, CODEBERG_BOT_USERNAMES→ FORGE_BOT_USERNAMES (with backwards-compat fallbacks) - Rename API helpers: codeberg_api()→forge_api(), codeberg_api_all() →forge_api_all() (with compat aliases) - Add forge_url field to project TOML; load-project.sh derives FORGE_API/FORGE_WEB from forge_url + repo - Update parse_repo_slug() to accept any host URL, not just codeberg - Forgejo data stored under ~/.disinto/forgejo/ (not in factory repo) - Update all 58 files: agent scripts, formulas, docs, site HTML Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
39d30faf45
commit
a66bd91721
58 changed files with 863 additions and 628 deletions
|
|
@ -6,10 +6,10 @@ sourced as needed.
|
|||
|
||||
| File | What it provides | Sourced by |
|
||||
|---|---|---|
|
||||
| `lib/env.sh` | Loads `.env`, sets `FACTORY_ROOT`, exports project config (`CODEBERG_REPO`, `PROJECT_NAME`, etc.), defines `log()`, `codeberg_api()`, `codeberg_api_all()` (accepts optional second TOKEN parameter, defaults to `$CODEBERG_TOKEN`), `woodpecker_api()`, `wpdb()`, `matrix_send()`, `matrix_send_ctx()`. Auto-loads project TOML if `PROJECT_TOML` is set. | Every agent |
|
||||
| `lib/env.sh` | Loads `.env`, sets `FACTORY_ROOT`, exports project config (`FORGE_REPO`, `PROJECT_NAME`, etc.), defines `log()`, `forge_api()`, `forge_api_all()` (accepts optional second TOKEN parameter, defaults to `$FORGE_TOKEN`), `woodpecker_api()`, `wpdb()`, `matrix_send()`, `matrix_send_ctx()`. Auto-loads project TOML if `PROJECT_TOML` is set. | Every agent |
|
||||
| `lib/ci-helpers.sh` | `ci_passed()` — returns 0 if CI state is "success" (or no CI configured). `ci_required_for_pr()` — returns 0 if PR has code files (CI required), 1 if non-code only (CI not required). `is_infra_step()` — returns 0 if a single CI step failure matches infra heuristics (clone/git exit 128, any exit 137, log timeout patterns). `classify_pipeline_failure()` — returns "infra \<reason>" if any failed Woodpecker step matches infra heuristics via `is_infra_step()`, else "code". `ensure_priority_label()` — looks up (or creates) the `priority` label and returns its ID; caches in `_PRIORITY_LABEL_ID`. | dev-poll, review-poll, review-pr, supervisor-poll |
|
||||
| `lib/ci-debug.sh` | CLI tool for Woodpecker CI: `list`, `status`, `logs`, `failures` subcommands. Not sourced — run directly. | Humans / dev-agent (tool access) |
|
||||
| `lib/load-project.sh` | Parses a `projects/*.toml` file into env vars (`PROJECT_NAME`, `CODEBERG_REPO`, `WOODPECKER_REPO_ID`, monitoring toggles, Matrix config, etc.). | env.sh (when `PROJECT_TOML` is set), supervisor-poll (per-project iteration) |
|
||||
| `lib/load-project.sh` | Parses a `projects/*.toml` file into env vars (`PROJECT_NAME`, `FORGE_REPO`, `WOODPECKER_REPO_ID`, monitoring toggles, Matrix config, etc.). | env.sh (when `PROJECT_TOML` is set), supervisor-poll (per-project iteration) |
|
||||
| `lib/parse-deps.sh` | Extracts dependency issue numbers from an issue body (stdin → stdout, one number per line). Matches `## Dependencies` / `## Depends on` / `## Blocked by` sections and inline `depends on #N` / `blocked by #N` patterns. Inline scan skips fenced code blocks to prevent false positives from code examples in issue bodies. Not sourced — executed via `bash lib/parse-deps.sh`. | dev-poll, supervisor-poll |
|
||||
| `lib/matrix_listener.sh` | Long-poll Matrix sync daemon. Dispatches thread replies to the correct agent via tmux session injection (dev, action, vault, review) or well-known files (`/tmp/{agent}-escalation-reply` for supervisor/gardener). Handles all agent reply routing. Run as systemd service. | Standalone daemon |
|
||||
| `lib/formula-session.sh` | `acquire_cron_lock()`, `check_memory()`, `load_formula()`, `build_context_block()`, `consume_escalation_reply()`, `start_formula_session()`, `formula_phase_callback()`, `build_prompt_footer()`, `run_formula_and_monitor(AGENT [TIMEOUT] [CALLBACK])` — shared helpers for formula-driven cron agents (lock, memory guard, formula loading, prompt assembly, tmux session, monitor loop, crash recovery). `formula_phase_callback()` handles `PHASE:escalate` (unified escalation path — kills the session; callers may follow up via Matrix). `run_formula_and_monitor` accepts an optional CALLBACK (default: `formula_phase_callback`) so callers can install custom merge-through or escalation handlers. | planner-run.sh, predictor-run.sh, gardener-run.sh, supervisor-run.sh, dev-agent.sh, action-agent.sh |
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ set -euo pipefail
|
|||
source "$(dirname "$0")/../lib/env.sh"
|
||||
|
||||
# WOODPECKER_TOKEN loaded from .env via env.sh
|
||||
REPO="${CODEBERG_REPO}"
|
||||
REPO="${FORGE_REPO}"
|
||||
API="${WOODPECKER_SERVER}/api/repos/${WOODPECKER_REPO_ID}"
|
||||
|
||||
api() {
|
||||
|
|
|
|||
|
|
@ -8,19 +8,19 @@ set -euo pipefail
|
|||
|
||||
# ensure_blocked_label_id — look up (or create) the "blocked" label, print its ID.
|
||||
# Caches the result in _BLOCKED_LABEL_ID to avoid repeated API calls.
|
||||
# Requires: CODEBERG_TOKEN, CODEBERG_API (from env.sh), codeberg_api()
|
||||
# Requires: FORGE_TOKEN, FORGE_API (from env.sh), forge_api()
|
||||
ensure_blocked_label_id() {
|
||||
if [ -n "${_BLOCKED_LABEL_ID:-}" ]; then
|
||||
printf '%s' "$_BLOCKED_LABEL_ID"
|
||||
return 0
|
||||
fi
|
||||
_BLOCKED_LABEL_ID=$(codeberg_api GET "/labels" 2>/dev/null \
|
||||
_BLOCKED_LABEL_ID=$(forge_api GET "/labels" 2>/dev/null \
|
||||
| jq -r '.[] | select(.name == "blocked") | .id' 2>/dev/null || true)
|
||||
if [ -z "$_BLOCKED_LABEL_ID" ]; then
|
||||
_BLOCKED_LABEL_ID=$(curl -sf -X POST \
|
||||
-H "Authorization: token ${CODEBERG_TOKEN}" \
|
||||
-H "Authorization: token ${FORGE_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${CODEBERG_API}/labels" \
|
||||
"${FORGE_API}/labels" \
|
||||
-d '{"name":"blocked","color":"#e11d48"}' 2>/dev/null \
|
||||
| jq -r '.id // empty' 2>/dev/null || true)
|
||||
fi
|
||||
|
|
@ -29,19 +29,19 @@ ensure_blocked_label_id() {
|
|||
|
||||
# ensure_priority_label — look up (or create) the "priority" label, print its ID.
|
||||
# Caches the result in _PRIORITY_LABEL_ID to avoid repeated API calls.
|
||||
# Requires: CODEBERG_TOKEN, CODEBERG_API (from env.sh), codeberg_api()
|
||||
# Requires: FORGE_TOKEN, FORGE_API (from env.sh), forge_api()
|
||||
ensure_priority_label() {
|
||||
if [ -n "${_PRIORITY_LABEL_ID:-}" ]; then
|
||||
printf '%s' "$_PRIORITY_LABEL_ID"
|
||||
return 0
|
||||
fi
|
||||
_PRIORITY_LABEL_ID=$(codeberg_api GET "/labels" 2>/dev/null \
|
||||
_PRIORITY_LABEL_ID=$(forge_api GET "/labels" 2>/dev/null \
|
||||
| jq -r '.[] | select(.name == "priority") | .id' 2>/dev/null || true)
|
||||
if [ -z "$_PRIORITY_LABEL_ID" ]; then
|
||||
_PRIORITY_LABEL_ID=$(curl -sf -X POST \
|
||||
-H "Authorization: token ${CODEBERG_TOKEN}" \
|
||||
-H "Authorization: token ${FORGE_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${CODEBERG_API}/labels" \
|
||||
"${FORGE_API}/labels" \
|
||||
-d '{"name":"priority","color":"#f59e0b"}' 2>/dev/null \
|
||||
| jq -r '.id // empty' 2>/dev/null || true)
|
||||
fi
|
||||
|
|
@ -68,7 +68,7 @@ diff_has_code_files() {
|
|||
ci_required_for_pr() {
|
||||
local pr_num="$1"
|
||||
local files all_json
|
||||
all_json=$(codeberg_api_all "/pulls/${pr_num}/files") || return 0
|
||||
all_json=$(forge_api_all "/pulls/${pr_num}/files") || return 0
|
||||
files=$(printf '%s' "$all_json" | jq -r '.[].filename' 2>/dev/null) || return 0
|
||||
if [ -z "$files" ]; then
|
||||
return 0 # empty file list — require CI as safety default
|
||||
|
|
@ -113,7 +113,7 @@ ci_failed() {
|
|||
is_infra_step() {
|
||||
local sname="$1" ecode="$2" log_data="${3:-}"
|
||||
|
||||
# Clone/git step exit 128 → Codeberg connection failure / rate limit
|
||||
# Clone/git step exit 128 → forge connection failure / rate limit
|
||||
if { [[ "$sname" == *clone* ]] || [[ "$sname" == git* ]]; } && [ "$ecode" = "128" ]; then
|
||||
echo "${sname} exit 128 (connection failure)"
|
||||
return 0
|
||||
|
|
|
|||
60
lib/env.sh
60
lib/env.sh
|
|
@ -24,17 +24,33 @@ 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)"
|
||||
# Forge token: new FORGE_TOKEN > legacy CODEBERG_TOKEN > ~/.netrc
|
||||
if [ -z "${FORGE_TOKEN:-}" ]; then
|
||||
FORGE_TOKEN="${CODEBERG_TOKEN:-}"
|
||||
fi
|
||||
export CODEBERG_TOKEN
|
||||
if [ -z "${FORGE_TOKEN:-}" ]; then
|
||||
FORGE_TOKEN="$(awk '/codeberg.org/{getline;getline;print $2}' ~/.netrc 2>/dev/null || true)"
|
||||
fi
|
||||
export FORGE_TOKEN
|
||||
export CODEBERG_TOKEN="${FORGE_TOKEN}" # backwards compat
|
||||
|
||||
# Project config
|
||||
export CODEBERG_REPO="${CODEBERG_REPO:-}"
|
||||
export CODEBERG_API="${CODEBERG_API:-https://codeberg.org/api/v1/repos/${CODEBERG_REPO}}"
|
||||
export CODEBERG_WEB="https://codeberg.org/${CODEBERG_REPO}"
|
||||
export PROJECT_NAME="${PROJECT_NAME:-${CODEBERG_REPO##*/}}"
|
||||
# Review bot token: FORGE_REVIEW_TOKEN > legacy REVIEW_BOT_TOKEN
|
||||
export FORGE_REVIEW_TOKEN="${FORGE_REVIEW_TOKEN:-${REVIEW_BOT_TOKEN:-}}"
|
||||
export REVIEW_BOT_TOKEN="${FORGE_REVIEW_TOKEN}" # backwards compat
|
||||
|
||||
# Bot usernames filter: FORGE_BOT_USERNAMES > legacy CODEBERG_BOT_USERNAMES
|
||||
export FORGE_BOT_USERNAMES="${FORGE_BOT_USERNAMES:-${CODEBERG_BOT_USERNAMES:-}}"
|
||||
export CODEBERG_BOT_USERNAMES="${FORGE_BOT_USERNAMES}" # backwards compat
|
||||
|
||||
# Project config (FORGE_* preferred, CODEBERG_* fallback)
|
||||
export FORGE_REPO="${FORGE_REPO:-${CODEBERG_REPO:-}}"
|
||||
export CODEBERG_REPO="${FORGE_REPO}" # backwards compat
|
||||
export FORGE_URL="${FORGE_URL:-http://localhost:3000}"
|
||||
export FORGE_API="${FORGE_API:-${FORGE_URL}/api/v1/repos/${FORGE_REPO}}"
|
||||
export FORGE_WEB="${FORGE_WEB:-${FORGE_URL}/${FORGE_REPO}}"
|
||||
export CODEBERG_API="${FORGE_API}" # backwards compat
|
||||
export CODEBERG_WEB="${FORGE_WEB}" # backwards compat
|
||||
export PROJECT_NAME="${PROJECT_NAME:-${FORGE_REPO##*/}}"
|
||||
export PROJECT_REPO_ROOT="${PROJECT_REPO_ROOT:-/home/${USER}/${PROJECT_NAME}}"
|
||||
export PRIMARY_BRANCH="${PRIMARY_BRANCH:-master}"
|
||||
export WOODPECKER_REPO_ID="${WOODPECKER_REPO_ID:-}"
|
||||
|
|
@ -46,23 +62,25 @@ log() {
|
|||
printf '[%s] %s\n' "$(date -u '+%Y-%m-%d %H:%M:%S UTC')" "$*"
|
||||
}
|
||||
|
||||
# Codeberg API helper — usage: codeberg_api GET /issues?state=open
|
||||
codeberg_api() {
|
||||
# Forge API helper — usage: forge_api GET /issues?state=open
|
||||
forge_api() {
|
||||
local method="$1" path="$2"
|
||||
shift 2
|
||||
curl -sf -X "$method" \
|
||||
-H "Authorization: token ${CODEBERG_TOKEN}" \
|
||||
-H "Authorization: token ${FORGE_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${CODEBERG_API}${path}" "$@"
|
||||
"${FORGE_API}${path}" "$@"
|
||||
}
|
||||
# Backwards-compat alias
|
||||
codeberg_api() { forge_api "$@"; }
|
||||
|
||||
# Paginate a Codeberg API GET endpoint and return all items as a merged JSON array.
|
||||
# Usage: codeberg_api_all /path (no existing query params)
|
||||
# codeberg_api_all /path?a=b (with existing params — appends &limit=50&page=N)
|
||||
# codeberg_api_all /path TOKEN (optional second arg: token; defaults to $CODEBERG_TOKEN)
|
||||
codeberg_api_all() {
|
||||
# Paginate a Forge API GET endpoint and return all items as a merged JSON array.
|
||||
# Usage: forge_api_all /path (no existing query params)
|
||||
# forge_api_all /path?a=b (with existing params — appends &limit=50&page=N)
|
||||
# forge_api_all /path TOKEN (optional second arg: token; defaults to $FORGE_TOKEN)
|
||||
forge_api_all() {
|
||||
local path_prefix="$1"
|
||||
local CODEBERG_TOKEN="${2:-${CODEBERG_TOKEN}}"
|
||||
local FORGE_TOKEN="${2:-${FORGE_TOKEN}}"
|
||||
local sep page page_items count all_items="[]"
|
||||
case "$path_prefix" in
|
||||
*"?"*) sep="&" ;;
|
||||
|
|
@ -70,7 +88,7 @@ codeberg_api_all() {
|
|||
esac
|
||||
page=1
|
||||
while true; do
|
||||
page_items=$(codeberg_api GET "${path_prefix}${sep}limit=50&page=${page}")
|
||||
page_items=$(forge_api GET "${path_prefix}${sep}limit=50&page=${page}")
|
||||
count=$(printf '%s' "$page_items" | jq 'length')
|
||||
[ "$count" -eq 0 ] && break
|
||||
all_items=$(printf '%s\n%s' "$all_items" "$page_items" | jq -s 'add')
|
||||
|
|
@ -79,6 +97,8 @@ codeberg_api_all() {
|
|||
done
|
||||
printf '%s' "$all_items"
|
||||
}
|
||||
# Backwards-compat alias
|
||||
codeberg_api_all() { forge_api_all "$@"; }
|
||||
|
||||
# Woodpecker API helper
|
||||
woodpecker_api() {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
# file-action-issue.sh — File an action issue for a formula run
|
||||
#
|
||||
# Usage: source this file, then call file_action_issue.
|
||||
# Requires: codeberg_api() from lib/env.sh, jq, lib/secret-scan.sh
|
||||
# Requires: forge_api() from lib/env.sh, jq, lib/secret-scan.sh
|
||||
#
|
||||
# file_action_issue <formula_name> <title> <body>
|
||||
# Sets FILED_ISSUE_NUM on success.
|
||||
|
|
@ -24,7 +24,7 @@ file_action_issue() {
|
|||
|
||||
# Dedup: skip if an open action issue for this formula already exists
|
||||
local open_actions
|
||||
open_actions=$(codeberg_api_all "/issues?state=open&type=issues&labels=action" 2>/dev/null || true)
|
||||
open_actions=$(forge_api_all "/issues?state=open&type=issues&labels=action" 2>/dev/null || true)
|
||||
if [ -n "$open_actions" ] && [ "$open_actions" != "null" ]; then
|
||||
local existing
|
||||
existing=$(printf '%s' "$open_actions" | \
|
||||
|
|
@ -36,7 +36,7 @@ file_action_issue() {
|
|||
|
||||
# Fetch 'action' label ID
|
||||
local action_label_id
|
||||
action_label_id=$(codeberg_api GET "/labels" 2>/dev/null | \
|
||||
action_label_id=$(forge_api GET "/labels" 2>/dev/null | \
|
||||
jq -r '.[] | select(.name == "action") | .id' 2>/dev/null || true)
|
||||
if [ -z "$action_label_id" ]; then
|
||||
return 2
|
||||
|
|
@ -50,7 +50,7 @@ file_action_issue() {
|
|||
--argjson labels "[$action_label_id]" \
|
||||
'{title: $title, body: $body, labels: $labels}')
|
||||
|
||||
result=$(codeberg_api POST "/issues" -d "$payload" 2>/dev/null || true)
|
||||
result=$(forge_api POST "/issues" -d "$payload" 2>/dev/null || true)
|
||||
FILED_ISSUE_NUM=$(printf '%s' "$result" | jq -r '.number // empty' 2>/dev/null || true)
|
||||
|
||||
if [ -z "$FILED_ISSUE_NUM" ]; then
|
||||
|
|
|
|||
|
|
@ -208,21 +208,21 @@ read_scratch_context() {
|
|||
# ── Prompt + monitor helpers ──────────────────────────────────────────────
|
||||
|
||||
# build_prompt_footer [EXTRA_API_LINES]
|
||||
# Assembles the common Codeberg API reference + environment + phase protocol
|
||||
# Assembles the common forge API reference + environment + phase protocol
|
||||
# block for formula prompts. Sets PROMPT_FOOTER.
|
||||
# Pass additional API endpoint lines (pre-formatted, newline-prefixed) via $1.
|
||||
# Requires globals: CODEBERG_API, FACTORY_ROOT, PROJECT_REPO_ROOT,
|
||||
# Requires globals: FORGE_API, FACTORY_ROOT, PROJECT_REPO_ROOT,
|
||||
# PRIMARY_BRANCH, PHASE_FILE.
|
||||
build_prompt_footer() {
|
||||
local extra_api="${1:-}"
|
||||
# shellcheck disable=SC2034 # consumed by the calling script's PROMPT
|
||||
PROMPT_FOOTER="## Codeberg API reference
|
||||
Base URL: ${CODEBERG_API}
|
||||
Auth header: -H \"Authorization: token \$CODEBERG_TOKEN\"
|
||||
Read issue: curl -sf -H \"Authorization: token \$CODEBERG_TOKEN\" '${CODEBERG_API}/issues/{number}' | jq '.body'
|
||||
Create issue: curl -sf -X POST -H \"Authorization: token \$CODEBERG_TOKEN\" -H 'Content-Type: application/json' '${CODEBERG_API}/issues' -d '{\"title\":\"...\",\"body\":\"...\",\"labels\":[LABEL_ID]}'${extra_api}
|
||||
List labels: curl -sf -H \"Authorization: token \$CODEBERG_TOKEN\" '${CODEBERG_API}/labels'
|
||||
NEVER echo or include the actual token value in output — always reference \$CODEBERG_TOKEN.
|
||||
PROMPT_FOOTER="## Forge API reference
|
||||
Base URL: ${FORGE_API}
|
||||
Auth header: -H \"Authorization: token \${FORGE_TOKEN}\"
|
||||
Read issue: curl -sf -H \"Authorization: token \${FORGE_TOKEN}\" '${FORGE_API}/issues/{number}' | jq '.body'
|
||||
Create issue: curl -sf -X POST -H \"Authorization: token \${FORGE_TOKEN}\" -H 'Content-Type: application/json' '${FORGE_API}/issues' -d '{\"title\":\"...\",\"body\":\"...\",\"labels\":[LABEL_ID]}'${extra_api}
|
||||
List labels: curl -sf -H \"Authorization: token \${FORGE_TOKEN}\" '${FORGE_API}/labels'
|
||||
NEVER echo or include the actual token value in output — always reference \${FORGE_TOKEN}.
|
||||
|
||||
## Environment
|
||||
FACTORY_ROOT=${FACTORY_ROOT}
|
||||
|
|
@ -240,7 +240,7 @@ On unrecoverable error:
|
|||
# run_formula_and_monitor AGENT_NAME [TIMEOUT]
|
||||
# Starts the formula session, injects PROMPT, monitors phase, and logs result.
|
||||
# Requires globals: SESSION_NAME, PHASE_FILE, PROJECT_REPO_ROOT, PROMPT,
|
||||
# CODEBERG_REPO, CLAUDE_MODEL (exported).
|
||||
# FORGE_REPO, CLAUDE_MODEL (exported).
|
||||
# shellcheck disable=SC2154 # SESSION_NAME, PHASE_FILE, PROJECT_REPO_ROOT, PROMPT set by caller
|
||||
run_formula_and_monitor() {
|
||||
local agent_name="$1"
|
||||
|
|
@ -258,7 +258,7 @@ run_formula_and_monitor() {
|
|||
|
||||
agent_inject_into_session "$SESSION_NAME" "$PROMPT"
|
||||
log "Prompt sent to tmux session"
|
||||
matrix_send "$agent_name" "${agent_name^} session started for ${CODEBERG_REPO}" 2>/dev/null || true
|
||||
matrix_send "$agent_name" "${agent_name^} session started for ${FORGE_REPO}" 2>/dev/null || true
|
||||
|
||||
log "Monitoring phase file: ${PHASE_FILE}"
|
||||
_FORMULA_CRASH_COUNT=0
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
# Blocks:
|
||||
# - git push --force / -f to primary branch
|
||||
# - rm -rf targeting paths outside the worktree
|
||||
# - Direct Codeberg API merge calls (should go through phase protocol)
|
||||
# - Direct forge API merge calls (should go through phase protocol)
|
||||
# - Direct issue close calls (should go through phase protocol)
|
||||
# - git checkout / git switch to primary branch (stay on feature branch)
|
||||
# - FACTORY_ROOT access from worktrees (formula agents exempted)
|
||||
|
|
@ -88,7 +88,7 @@ if [ -n "$worktree_path" ] \
|
|||
fi
|
||||
fi
|
||||
|
||||
# --- Guard 3: Direct Codeberg API merge calls ---
|
||||
# --- Guard 3: Direct forge API merge calls ---
|
||||
if printf '%s' "$command_str" | grep -qE '/pulls/[0-9]+/merge'; then
|
||||
printf 'BLOCKED: Direct API merge calls must go through the phase protocol. Push your changes and write PHASE:awaiting_ci — the orchestrator handles merges.\n'
|
||||
exit 2
|
||||
|
|
|
|||
|
|
@ -5,9 +5,11 @@
|
|||
# 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
|
||||
# PROJECT_NAME, FORGE_REPO, FORGE_API, FORGE_WEB, FORGE_URL,
|
||||
# PROJECT_REPO_ROOT, PRIMARY_BRANCH, WOODPECKER_REPO_ID,
|
||||
# PROJECT_CONTAINERS, CHECK_PRS, CHECK_DEV_AGENT,
|
||||
# CHECK_PIPELINE_STALL, CI_STALE_MINUTES
|
||||
# (plus backwards-compat aliases: CODEBERG_REPO, CODEBERG_API, CODEBERG_WEB)
|
||||
#
|
||||
# If no argument given, does nothing (allows poll scripts to work with
|
||||
# plain .env fallback for backwards compatibility).
|
||||
|
|
@ -35,7 +37,8 @@ def emit(key, val):
|
|||
|
||||
# Top-level
|
||||
emit('PROJECT_NAME', cfg.get('name', ''))
|
||||
emit('CODEBERG_REPO', cfg.get('repo', ''))
|
||||
emit('FORGE_REPO', cfg.get('repo', ''))
|
||||
emit('FORGE_URL', cfg.get('forge_url', ''))
|
||||
|
||||
if 'repo_root' in cfg:
|
||||
emit('PROJECT_REPO_ROOT', cfg['repo_root'])
|
||||
|
|
@ -79,11 +82,17 @@ while IFS='=' read -r _key _val; do
|
|||
export "$_key=$_val"
|
||||
done <<< "$_PROJECT_VARS"
|
||||
|
||||
# Derive CODEBERG_API and CODEBERG_WEB if repo changed
|
||||
if [ -n "$CODEBERG_REPO" ]; then
|
||||
export CODEBERG_API="https://codeberg.org/api/v1/repos/${CODEBERG_REPO}"
|
||||
export CODEBERG_WEB="https://codeberg.org/${CODEBERG_REPO}"
|
||||
# Derive FORGE_API and FORGE_WEB from forge_url + repo
|
||||
# FORGE_URL: TOML forge_url > existing FORGE_URL > default
|
||||
export FORGE_URL="${FORGE_URL:-http://localhost:3000}"
|
||||
if [ -n "$FORGE_REPO" ]; then
|
||||
export FORGE_API="${FORGE_URL}/api/v1/repos/${FORGE_REPO}"
|
||||
export FORGE_WEB="${FORGE_URL}/${FORGE_REPO}"
|
||||
fi
|
||||
# Backwards-compat aliases
|
||||
export CODEBERG_REPO="${FORGE_REPO}"
|
||||
export CODEBERG_API="${FORGE_API:-}"
|
||||
export CODEBERG_WEB="${FORGE_WEB:-}"
|
||||
|
||||
# Derive PROJECT_REPO_ROOT if not explicitly set
|
||||
if [ -z "${PROJECT_REPO_ROOT:-}" ] && [ -n "${PROJECT_NAME:-}" ]; then
|
||||
|
|
|
|||
|
|
@ -33,8 +33,9 @@ _SAFE_PATTERNS=(
|
|||
# Git SHAs in typical git contexts (commit refs, not standalone secrets)
|
||||
'commit [0-9a-f]{40}'
|
||||
'Merge [0-9a-f]{40}'
|
||||
# Codeberg/GitHub URLs with short hex (PR refs, commit links)
|
||||
# Forge/GitHub URLs with short hex (PR refs, commit links)
|
||||
'codeberg\.org/[^[:space:]]+'
|
||||
'localhost:3000/[^[:space:]]+'
|
||||
# ShellCheck directive codes
|
||||
'SC[0-9]{4}'
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue