211 lines
6.3 KiB
Bash
211 lines
6.3 KiB
Bash
|
|
#!/usr/bin/env bash
|
||
|
|
# profile.sh — Helpers for agent .profile repo management
|
||
|
|
#
|
||
|
|
# Source after lib/env.sh and lib/formula-session.sh:
|
||
|
|
# source "$(dirname "$0")/../lib/env.sh"
|
||
|
|
# source "$(dirname "$0")/lib/formula-session.sh"
|
||
|
|
# source "$(dirname "$0")/lib/profile.sh"
|
||
|
|
#
|
||
|
|
# Required globals: FORGE_TOKEN, FORGE_URL, AGENT_IDENTITY, PROFILE_REPO_PATH
|
||
|
|
#
|
||
|
|
# Functions:
|
||
|
|
# profile_propose_formula NEW_FORMULA CONTENT REASON — create PR to update formula.toml
|
||
|
|
|
||
|
|
set -euo pipefail
|
||
|
|
|
||
|
|
# Internal log helper
|
||
|
|
_profile_log() {
|
||
|
|
if declare -f log >/dev/null 2>&1; then
|
||
|
|
log "profile: $*"
|
||
|
|
else
|
||
|
|
printf '[%s] profile: %s\n' "$(date -u '+%Y-%m-%d %H:%M:%S UTC')" "$*" >&2
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
# -----------------------------------------------------------------------------
|
||
|
|
# profile_propose_formula — Propose a formula change via PR
|
||
|
|
#
|
||
|
|
# Creates a branch, writes updated formula.toml, opens a PR, and returns PR number.
|
||
|
|
# Branch is protected (requires admin approval per #87).
|
||
|
|
#
|
||
|
|
# Args:
|
||
|
|
# $1 - NEW_FORMULA_CONTENT: The complete new formula.toml content
|
||
|
|
# $2 - REASON: Human-readable explanation of what changed and why
|
||
|
|
#
|
||
|
|
# Returns:
|
||
|
|
# 0 on success, prints PR number to stdout
|
||
|
|
# 1 on failure
|
||
|
|
#
|
||
|
|
# Example:
|
||
|
|
# source "$(dirname "$0")/../lib/env.sh"
|
||
|
|
# source "$(dirname "$0")/lib/formula-session.sh"
|
||
|
|
# source "$(dirname "$0")/lib/profile.sh"
|
||
|
|
# AGENT_IDENTITY="dev-bot"
|
||
|
|
# ensure_profile_repo "$AGENT_IDENTITY"
|
||
|
|
# profile_propose_formula "$new_formula" "Added new prompt pattern for code review"
|
||
|
|
# -----------------------------------------------------------------------------
|
||
|
|
profile_propose_formula() {
|
||
|
|
local new_formula="$1"
|
||
|
|
local reason="$2"
|
||
|
|
|
||
|
|
if [ -z "${AGENT_IDENTITY:-}" ]; then
|
||
|
|
_profile_log "ERROR: AGENT_IDENTITY not set"
|
||
|
|
return 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
if [ -z "${PROFILE_REPO_PATH:-}" ]; then
|
||
|
|
_profile_log "ERROR: PROFILE_REPO_PATH not set — ensure_profile_repo not called"
|
||
|
|
return 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
if [ -z "${FORGE_TOKEN:-}" ]; then
|
||
|
|
_profile_log "ERROR: FORGE_TOKEN not set"
|
||
|
|
return 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
if [ -z "${FORGE_URL:-}" ]; then
|
||
|
|
_profile_log "ERROR: FORGE_URL not set"
|
||
|
|
return 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Generate short description from reason for branch name
|
||
|
|
local short_desc
|
||
|
|
short_desc=$(printf '%s' "$reason" | \
|
||
|
|
tr '[:upper:]' '[:lower:]' | \
|
||
|
|
sed 's/[^a-z0-9 ]//g' | \
|
||
|
|
sed 's/ */ /g' | \
|
||
|
|
sed 's/^ *//;s/ *$//' | \
|
||
|
|
cut -c1-40 | \
|
||
|
|
tr ' ' '-')
|
||
|
|
|
||
|
|
if [ -z "$short_desc" ]; then
|
||
|
|
short_desc="formula-update"
|
||
|
|
fi
|
||
|
|
|
||
|
|
local branch_name="formula/${short_desc}"
|
||
|
|
local formula_path="${PROFILE_REPO_PATH}/formula.toml"
|
||
|
|
|
||
|
|
_profile_log "Proposing formula change: ${branch_name}"
|
||
|
|
_profile_log "Reason: ${reason}"
|
||
|
|
|
||
|
|
# Ensure we're on main branch and up-to-date
|
||
|
|
_profile_log "Fetching .profile repo"
|
||
|
|
(
|
||
|
|
cd "$PROFILE_REPO_PATH" || return 1
|
||
|
|
|
||
|
|
git fetch origin main --quiet 2>/dev/null || \
|
||
|
|
git fetch origin master --quiet 2>/dev/null || true
|
||
|
|
|
||
|
|
# Reset to main/master
|
||
|
|
if git checkout main --quiet 2>/dev/null; then
|
||
|
|
git pull --ff-only origin main --quiet 2>/dev/null || true
|
||
|
|
elif git checkout master --quiet 2>/dev/null; then
|
||
|
|
git pull --ff-only origin master --quiet 2>/dev/null || true
|
||
|
|
else
|
||
|
|
_profile_log "ERROR: Failed to checkout main/master branch"
|
||
|
|
return 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Create and checkout new branch
|
||
|
|
git checkout -b "$branch_name" 2>/dev/null || {
|
||
|
|
_profile_log "Branch ${branch_name} may already exist"
|
||
|
|
git checkout "$branch_name" 2>/dev/null || return 1
|
||
|
|
}
|
||
|
|
|
||
|
|
# Write formula.toml
|
||
|
|
printf '%s' "$new_formula" > "$formula_path"
|
||
|
|
|
||
|
|
# Commit the change
|
||
|
|
git config user.name "${AGENT_IDENTITY}" || true
|
||
|
|
git config user.email "${AGENT_IDENTITY}@users.noreply.codeberg.org" || true
|
||
|
|
|
||
|
|
git add "$formula_path"
|
||
|
|
git commit -m "formula: ${reason}" --no-verify || {
|
||
|
|
_profile_log "No changes to commit (formula unchanged)"
|
||
|
|
# Check if branch has any commits
|
||
|
|
if git rev-parse HEAD >/dev/null 2>&1; then
|
||
|
|
: # branch has commits, continue
|
||
|
|
else
|
||
|
|
_profile_log "ERROR: Failed to create commit"
|
||
|
|
return 1
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
# Push branch
|
||
|
|
local remote="${FORGE_REMOTE:-origin}"
|
||
|
|
git push --set-upstream "$remote" "$branch_name" --quiet 2>/dev/null || {
|
||
|
|
_profile_log "ERROR: Failed to push branch"
|
||
|
|
return 1
|
||
|
|
}
|
||
|
|
|
||
|
|
_profile_log "Branch pushed: ${branch_name}"
|
||
|
|
|
||
|
|
# Create PR
|
||
|
|
local forge_url="${FORGE_URL%/}"
|
||
|
|
local api_url="${forge_url}/api/v1/repos/${AGENT_IDENTITY}/.profile"
|
||
|
|
local primary_branch="main"
|
||
|
|
|
||
|
|
# Check if main or master is the primary branch
|
||
|
|
if ! curl -sf -o /dev/null -w "%{http_code}" \
|
||
|
|
-H "Authorization: token ${FORGE_TOKEN}" \
|
||
|
|
"${api_url}/git/branches/main" 2>/dev/null | grep -q "200"; then
|
||
|
|
primary_branch="master"
|
||
|
|
fi
|
||
|
|
|
||
|
|
local pr_title="formula: ${reason}"
|
||
|
|
local pr_body="# Formula Update
|
||
|
|
|
||
|
|
**Reason:** ${reason}
|
||
|
|
|
||
|
|
---
|
||
|
|
*This PR was auto-generated by ${AGENT_IDENTITY}.*
|
||
|
|
"
|
||
|
|
|
||
|
|
local pr_response http_code
|
||
|
|
local pr_json
|
||
|
|
pr_json=$(jq -n \
|
||
|
|
--arg t "$pr_title" \
|
||
|
|
--arg b "$pr_body" \
|
||
|
|
--arg h "$branch_name" \
|
||
|
|
--arg base "$primary_branch" \
|
||
|
|
'{title:$t, body:$b, head:$h, base:$base}') || {
|
||
|
|
_profile_log "ERROR: Failed to build PR JSON"
|
||
|
|
return 1
|
||
|
|
}
|
||
|
|
|
||
|
|
pr_response=$(curl -s -w "\n%{http_code}" -X POST \
|
||
|
|
-H "Authorization: token ${FORGE_TOKEN}" \
|
||
|
|
-H "Content-Type: application/json" \
|
||
|
|
"${api_url}/pulls" \
|
||
|
|
-d "$pr_json" || true)
|
||
|
|
|
||
|
|
http_code=$(printf '%s\n' "$pr_response" | tail -1)
|
||
|
|
pr_response=$(printf '%s\n' "$pr_response" | sed '$d')
|
||
|
|
|
||
|
|
if [ "$http_code" = "201" ] || [ "$http_code" = "200" ]; then
|
||
|
|
local pr_num
|
||
|
|
pr_num=$(printf '%s' "$pr_response" | jq -r '.number')
|
||
|
|
_profile_log "PR created: #${pr_num}"
|
||
|
|
printf '%s' "$pr_num"
|
||
|
|
return 0
|
||
|
|
else
|
||
|
|
# Check if PR already exists (409 conflict)
|
||
|
|
if [ "$http_code" = "409" ]; then
|
||
|
|
local existing_pr
|
||
|
|
existing_pr=$(curl -sf -H "Authorization: token ${FORGE_TOKEN}" \
|
||
|
|
"${api_url}/pulls?state=open&head=${AGENT_IDENTITY}:formula/${short_desc}" 2>/dev/null | \
|
||
|
|
jq -r '.[0].number // empty') || true
|
||
|
|
if [ -n "$existing_pr" ]; then
|
||
|
|
_profile_log "PR already exists: #${existing_pr}"
|
||
|
|
printf '%s' "$existing_pr"
|
||
|
|
return 0
|
||
|
|
fi
|
||
|
|
fi
|
||
|
|
_profile_log "ERROR: Failed to create PR (HTTP ${http_code})"
|
||
|
|
return 1
|
||
|
|
fi
|
||
|
|
)
|
||
|
|
|
||
|
|
return $?
|
||
|
|
}
|