Fixes #757 ## Changes Separate operations from code into {project}-ops repo pattern. Added OPS_REPO_ROOT infrastructure (env.sh, load-project.sh, formula-session.sh with ensure_ops_repo helper). Updated all 8 agent scripts and 7 formulas to read/write vault items, journals, evidence, prerequisites, RESOURCES.md, and knowledge from the ops repo. Added setup_ops_repo() to disinto init for automatic ops repo creation and seeding. Removed migrated data from code repo (vault data dirs, planner journal/memory/prerequisites, supervisor journal/best-practices, evidence, RESOURCES.md). Updated all documentation. 55 files changed, ShellCheck clean, all 38 phase tests pass. Co-authored-by: openhands <openhands@all-hands.dev> Reviewed-on: https://codeberg.org/johba/disinto/pulls/767 Reviewed-by: Disinto_bot <disinto_bot@noreply.codeberg.org>
This commit is contained in:
parent
a899fd0733
commit
71fe89cdd0
55 changed files with 421 additions and 932 deletions
|
|
@ -10,11 +10,11 @@ to a human by writing `PHASE:escalate` to a phase file — using the same
|
|||
unified escalation path as dev/action agents.
|
||||
|
||||
**Pipeline B — Procurement (*.md)**: The planner files resource requests as
|
||||
markdown files in `vault/pending/`. `vault-poll.sh` notifies the human via
|
||||
markdown files in `$OPS_REPO_ROOT/vault/pending/`. `vault-poll.sh` notifies the human via
|
||||
vault/forge. The human fulfills the request (creates accounts, provisions infra,
|
||||
adds secrets to `.env`) and moves the file to `vault/approved/`.
|
||||
adds secrets to `.env`) and moves the file to `$OPS_REPO_ROOT/vault/approved/`.
|
||||
`vault-fire.sh` then extracts the proposed entry and appends it to
|
||||
`RESOURCES.md`.
|
||||
`$OPS_REPO_ROOT/RESOURCES.md`.
|
||||
|
||||
**Pipeline C — Rent-a-Human (outreach drafts)**: Any agent can dispatch the
|
||||
`run-rent-a-human` formula (via an `action` issue) when a task requires a human
|
||||
|
|
@ -30,15 +30,15 @@ needed — the human reviews and publishes directly.
|
|||
- `vault/vault-agent.sh` — Classifies and routes pending JSON actions via `claude -p`: auto-approve, auto-reject, or escalate to human
|
||||
- `vault/vault-env.sh` — Shared env setup for vault sub-scripts: sources `lib/env.sh`, overrides `FORGE_TOKEN` with `FORGE_VAULT_TOKEN`, sets `VAULT_TOKEN` for vault-runner container
|
||||
- `vault/PROMPT.md` — System prompt for the vault agent's Claude invocation
|
||||
- `vault/vault-fire.sh` — Executes an approved action (JSON) in an **ephemeral Docker container** with vault-only secrets injected (GITHUB_TOKEN, CLAWHUB_TOKEN — never exposed to agents). For deployment actions, calls `lib/ci-helpers.sh:ci_promote()` to gate production promotes via Woodpecker environments. Writes RESOURCES.md entry for procurement MD approvals.
|
||||
- `vault/vault-fire.sh` — Executes an approved action (JSON) in an **ephemeral Docker container** with vault-only secrets injected (GITHUB_TOKEN, CLAWHUB_TOKEN — never exposed to agents). For deployment actions, calls `lib/ci-helpers.sh:ci_promote()` to gate production promotes via Woodpecker environments. Writes `$OPS_REPO_ROOT/RESOURCES.md` entry for procurement MD approvals.
|
||||
- `vault/vault-reject.sh` — Marks a JSON action as rejected
|
||||
- `formulas/run-rent-a-human.toml` — Formula for human-action drafts: Claude researches target platform norms, drafts copy-paste content, writes to `vault/outreach/{platform}/drafts/`, notifies human via vault/forge
|
||||
|
||||
**Procurement flow**:
|
||||
1. Planner drops `vault/pending/<name>.md` with what/why/proposed RESOURCES.md entry
|
||||
**Procurement flow** (all vault items live in `$OPS_REPO_ROOT/vault/`):
|
||||
1. Planner drops `$OPS_REPO_ROOT/vault/pending/<name>.md` with what/why/proposed RESOURCES.md entry
|
||||
2. `vault-poll.sh` notifies human via vault/forge
|
||||
3. Human fulfills: creates account, adds secrets to `.env`, moves file to `vault/approved/`
|
||||
4. `vault-fire.sh` extracts proposed entry, appends to RESOURCES.md, moves to `vault/fired/`
|
||||
3. Human fulfills: creates account, adds secrets to `.env`, moves file to `approved/`
|
||||
4. `vault-fire.sh` extracts proposed entry, appends to `$OPS_REPO_ROOT/RESOURCES.md`, moves to `fired/`
|
||||
5. Next planner run reads RESOURCES.md → new capability available → unblocks prerequisite tree
|
||||
|
||||
**Environment variables consumed**:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Vault Agent
|
||||
|
||||
You are the vault agent for `$FORGE_REPO`. You were called by
|
||||
`vault-poll.sh` because one or more actions in `vault/pending/` need
|
||||
`vault-poll.sh` because one or more actions in `$OPS_REPO_ROOT/vault/pending/` need
|
||||
classification and routing.
|
||||
|
||||
## Two Pipelines
|
||||
|
|
@ -16,7 +16,7 @@ You classify and route these: auto-approve, escalate, or reject.
|
|||
Resource requests from the planner. These always escalate to the human —
|
||||
you do NOT auto-approve or reject procurement requests. The human fulfills
|
||||
the request (creates accounts, provisions infra, adds secrets to .env)
|
||||
and moves the file from `vault/pending/` to `vault/approved/`.
|
||||
and moves the file from `$OPS_REPO_ROOT/vault/pending/` to `$OPS_REPO_ROOT/vault/approved/`.
|
||||
`vault-fire.sh` then writes the RESOURCES.md entry.
|
||||
|
||||
## Your Job (Action Gating only)
|
||||
|
|
@ -116,7 +116,7 @@ ROUTE: <action-id> → <auto-approve|escalate|reject> — <reason>
|
|||
|
||||
- Process ALL pending JSON actions in the batch. Never skip silently.
|
||||
- For auto-approved actions, fire them immediately via `vault-fire.sh`.
|
||||
- For escalated actions, move to `vault/approved/` only AFTER human approval.
|
||||
- For escalated actions, move to `$OPS_REPO_ROOT/vault/approved/` only AFTER human approval.
|
||||
- Read the action JSON carefully. Check the payload, not just the metadata.
|
||||
- Ignore `.md` files in pending/ — those are procurement requests handled
|
||||
separately by vault-poll.sh and the human.
|
||||
|
|
|
|||
|
|
@ -13,9 +13,10 @@ set -euo pipefail
|
|||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
source "${SCRIPT_DIR}/vault-env.sh"
|
||||
|
||||
VAULT_DIR="${FACTORY_ROOT}/vault"
|
||||
PROMPT_FILE="${VAULT_DIR}/PROMPT.md"
|
||||
LOGFILE="${VAULT_DIR}/vault.log"
|
||||
VAULT_SCRIPT_DIR="${FACTORY_ROOT}/vault"
|
||||
OPS_VAULT_DIR="${OPS_REPO_ROOT}/vault"
|
||||
PROMPT_FILE="${VAULT_SCRIPT_DIR}/PROMPT.md"
|
||||
LOGFILE="${VAULT_SCRIPT_DIR}/vault.log"
|
||||
CLAUDE_TIMEOUT="${CLAUDE_TIMEOUT:-3600}"
|
||||
|
||||
log() {
|
||||
|
|
@ -26,7 +27,7 @@ log() {
|
|||
ACTIONS_BATCH=""
|
||||
ACTION_COUNT=0
|
||||
|
||||
for action_file in "${VAULT_DIR}/pending/"*.json; do
|
||||
for action_file in "${OPS_VAULT_DIR}/pending/"*.json; do
|
||||
[ -f "$action_file" ] || continue
|
||||
|
||||
ACTION_STATUS=$(jq -r '.status // ""' < "$action_file" 2>/dev/null)
|
||||
|
|
@ -36,7 +37,7 @@ for action_file in "${VAULT_DIR}/pending/"*.json; do
|
|||
if ! jq empty < "$action_file" 2>/dev/null; then
|
||||
ACTION_ID=$(basename "$action_file" .json)
|
||||
log "malformed JSON: $action_file — rejecting"
|
||||
bash "${VAULT_DIR}/vault-reject.sh" "$ACTION_ID" "malformed JSON" 2>/dev/null || true
|
||||
bash "${VAULT_SCRIPT_DIR}/vault-reject.sh" "$ACTION_ID" "malformed JSON" 2>/dev/null || true
|
||||
continue
|
||||
fi
|
||||
|
||||
|
|
@ -66,9 +67,10 @@ ${ACTIONS_BATCH}
|
|||
|
||||
## Environment
|
||||
- FACTORY_ROOT=${FACTORY_ROOT}
|
||||
- Vault directory: ${VAULT_DIR}
|
||||
- vault-fire.sh: bash ${VAULT_DIR}/vault-fire.sh <action-id>
|
||||
- vault-reject.sh: bash ${VAULT_DIR}/vault-reject.sh <action-id> \"<reason>\"
|
||||
- OPS_REPO_ROOT=${OPS_REPO_ROOT}
|
||||
- Vault data: ${OPS_VAULT_DIR}
|
||||
- vault-fire.sh: bash ${VAULT_SCRIPT_DIR}/vault-fire.sh <action-id>
|
||||
- vault-reject.sh: bash ${VAULT_SCRIPT_DIR}/vault-reject.sh <action-id> \"<reason>\"
|
||||
|
||||
Process each action now. For auto-approve, fire immediately. For reject, call vault-reject.sh.
|
||||
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ set -euo pipefail
|
|||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
source "${SCRIPT_DIR}/vault-env.sh"
|
||||
|
||||
VAULT_DIR="${FACTORY_ROOT}/vault"
|
||||
LOCKS_DIR="${VAULT_DIR}/.locks"
|
||||
LOGFILE="${VAULT_DIR}/vault.log"
|
||||
RESOURCES_FILE="${PROJECT_REPO_ROOT:-${FACTORY_ROOT}}/RESOURCES.md"
|
||||
OPS_VAULT_DIR="${OPS_REPO_ROOT}/vault"
|
||||
LOCKS_DIR="${FACTORY_ROOT}/vault/.locks"
|
||||
LOGFILE="${FACTORY_ROOT}/vault/vault.log"
|
||||
RESOURCES_FILE="${OPS_REPO_ROOT}/RESOURCES.md"
|
||||
|
||||
log() {
|
||||
printf '[%s] vault-fire: %s\n' "$(date -u '+%Y-%m-%d %H:%M:%S UTC')" "$*" >> "$LOGFILE"
|
||||
|
|
@ -34,19 +34,19 @@ ACTION_ID="${1:?Usage: vault-fire.sh <item-id>}"
|
|||
IS_PROCUREMENT=false
|
||||
ACTION_FILE=""
|
||||
|
||||
if [ -f "${VAULT_DIR}/approved/${ACTION_ID}.md" ]; then
|
||||
if [ -f "${OPS_VAULT_DIR}/approved/${ACTION_ID}.md" ]; then
|
||||
IS_PROCUREMENT=true
|
||||
ACTION_FILE="${VAULT_DIR}/approved/${ACTION_ID}.md"
|
||||
elif [ -f "${VAULT_DIR}/pending/${ACTION_ID}.md" ]; then
|
||||
ACTION_FILE="${OPS_VAULT_DIR}/approved/${ACTION_ID}.md"
|
||||
elif [ -f "${OPS_VAULT_DIR}/pending/${ACTION_ID}.md" ]; then
|
||||
IS_PROCUREMENT=true
|
||||
mv "${VAULT_DIR}/pending/${ACTION_ID}.md" "${VAULT_DIR}/approved/${ACTION_ID}.md"
|
||||
ACTION_FILE="${VAULT_DIR}/approved/${ACTION_ID}.md"
|
||||
mv "${OPS_VAULT_DIR}/pending/${ACTION_ID}.md" "${OPS_VAULT_DIR}/approved/${ACTION_ID}.md"
|
||||
ACTION_FILE="${OPS_VAULT_DIR}/approved/${ACTION_ID}.md"
|
||||
log "$ACTION_ID: pending → approved (procurement)"
|
||||
elif [ -f "${VAULT_DIR}/approved/${ACTION_ID}.json" ]; then
|
||||
ACTION_FILE="${VAULT_DIR}/approved/${ACTION_ID}.json"
|
||||
elif [ -f "${VAULT_DIR}/pending/${ACTION_ID}.json" ]; then
|
||||
mv "${VAULT_DIR}/pending/${ACTION_ID}.json" "${VAULT_DIR}/approved/${ACTION_ID}.json"
|
||||
ACTION_FILE="${VAULT_DIR}/approved/${ACTION_ID}.json"
|
||||
elif [ -f "${OPS_VAULT_DIR}/approved/${ACTION_ID}.json" ]; then
|
||||
ACTION_FILE="${OPS_VAULT_DIR}/approved/${ACTION_ID}.json"
|
||||
elif [ -f "${OPS_VAULT_DIR}/pending/${ACTION_ID}.json" ]; then
|
||||
mv "${OPS_VAULT_DIR}/pending/${ACTION_ID}.json" "${OPS_VAULT_DIR}/approved/${ACTION_ID}.json"
|
||||
ACTION_FILE="${OPS_VAULT_DIR}/approved/${ACTION_ID}.json"
|
||||
TMP=$(mktemp)
|
||||
jq '.status = "approved"' "$ACTION_FILE" > "$TMP" && mv "$TMP" "$ACTION_FILE"
|
||||
log "$ACTION_ID: pending → approved"
|
||||
|
|
@ -93,7 +93,7 @@ if [ "$IS_PROCUREMENT" = true ]; then
|
|||
log "$ACTION_ID: wrote RESOURCES.md entry"
|
||||
|
||||
# Move to fired/
|
||||
mv "$ACTION_FILE" "${VAULT_DIR}/fired/${ACTION_ID}.md"
|
||||
mv "$ACTION_FILE" "${OPS_VAULT_DIR}/fired/${ACTION_ID}.md"
|
||||
rm -f "${LOCKS_DIR}/${ACTION_ID}.notified"
|
||||
log "$ACTION_ID: approved → fired (procurement)"
|
||||
exit 0
|
||||
|
|
@ -122,7 +122,7 @@ if [ -f "${FACTORY_ROOT}/.env.vault.enc" ] && [ -f "${FACTORY_ROOT}/docker-compo
|
|||
else
|
||||
# Fallback for bare-metal or pre-migration setups: run action handler directly
|
||||
log "$ACTION_ID: no .env.vault.enc or docker-compose.yml — running action directly"
|
||||
bash "${VAULT_DIR}/vault-run-action.sh" "$ACTION_ID" >> "$LOGFILE" 2>&1 || FIRE_EXIT=$?
|
||||
bash "${SCRIPT_DIR}/vault-run-action.sh" "$ACTION_ID" >> "$LOGFILE" 2>&1 || FIRE_EXIT=$?
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
|
|
@ -132,7 +132,7 @@ if [ "$FIRE_EXIT" -eq 0 ]; then
|
|||
# Update with fired timestamp and move to fired/
|
||||
TMP=$(mktemp)
|
||||
jq --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" '.status = "fired" | .fired_at = $ts' "$ACTION_FILE" > "$TMP" \
|
||||
&& mv "$TMP" "${VAULT_DIR}/fired/${ACTION_ID}.json"
|
||||
&& mv "$TMP" "${OPS_VAULT_DIR}/fired/${ACTION_ID}.json"
|
||||
rm -f "$ACTION_FILE"
|
||||
log "$ACTION_ID: approved → fired"
|
||||
else
|
||||
|
|
|
|||
|
|
@ -26,8 +26,9 @@ FORGE_TOKEN="${FORGE_VAULT_TOKEN:-${FORGE_TOKEN}}"
|
|||
LOGFILE="${FACTORY_ROOT}/vault/vault.log"
|
||||
STATUSFILE="/tmp/vault-status"
|
||||
LOCKFILE="/tmp/vault-poll.lock"
|
||||
VAULT_DIR="${FACTORY_ROOT}/vault"
|
||||
LOCKS_DIR="${VAULT_DIR}/.locks"
|
||||
VAULT_SCRIPT_DIR="${FACTORY_ROOT}/vault"
|
||||
OPS_VAULT_DIR="${OPS_REPO_ROOT}/vault"
|
||||
LOCKS_DIR="${VAULT_SCRIPT_DIR}/.locks"
|
||||
|
||||
TIMEOUT_HOURS=48
|
||||
|
||||
|
|
@ -78,7 +79,7 @@ unlock_action() {
|
|||
# =============================================================================
|
||||
status "phase 1: retrying approved items"
|
||||
|
||||
for action_file in "${VAULT_DIR}/approved/"*.json; do
|
||||
for action_file in "${OPS_VAULT_DIR}/approved/"*.json; do
|
||||
[ -f "$action_file" ] || continue
|
||||
ACTION_ID=$(jq -r '.id // ""' < "$action_file" 2>/dev/null)
|
||||
[ -z "$ACTION_ID" ] && continue
|
||||
|
|
@ -89,7 +90,7 @@ for action_file in "${VAULT_DIR}/approved/"*.json; do
|
|||
fi
|
||||
|
||||
log "retrying approved action: $ACTION_ID"
|
||||
if bash "${VAULT_DIR}/vault-fire.sh" "$ACTION_ID" >> "$LOGFILE" 2>&1; then
|
||||
if bash "${VAULT_SCRIPT_DIR}/vault-fire.sh" "$ACTION_ID" >> "$LOGFILE" 2>&1; then
|
||||
log "fired $ACTION_ID (retry)"
|
||||
else
|
||||
log "ERROR: fire failed for $ACTION_ID (retry)"
|
||||
|
|
@ -99,7 +100,7 @@ for action_file in "${VAULT_DIR}/approved/"*.json; do
|
|||
done
|
||||
|
||||
# Retry approved procurement requests (.md)
|
||||
for req_file in "${VAULT_DIR}/approved/"*.md; do
|
||||
for req_file in "${OPS_VAULT_DIR}/approved/"*.md; do
|
||||
[ -f "$req_file" ] || continue
|
||||
REQ_ID=$(basename "$req_file" .md)
|
||||
|
||||
|
|
@ -109,7 +110,7 @@ for req_file in "${VAULT_DIR}/approved/"*.md; do
|
|||
fi
|
||||
|
||||
log "retrying approved procurement: $REQ_ID"
|
||||
if bash "${VAULT_DIR}/vault-fire.sh" "$REQ_ID" >> "$LOGFILE" 2>&1; then
|
||||
if bash "${VAULT_SCRIPT_DIR}/vault-fire.sh" "$REQ_ID" >> "$LOGFILE" 2>&1; then
|
||||
log "fired procurement $REQ_ID (retry)"
|
||||
else
|
||||
log "ERROR: fire failed for procurement $REQ_ID (retry)"
|
||||
|
|
@ -126,7 +127,7 @@ status "phase 2: checking escalation timeouts"
|
|||
NOW_EPOCH=$(date +%s)
|
||||
TIMEOUT_SECS=$((TIMEOUT_HOURS * 3600))
|
||||
|
||||
for action_file in "${VAULT_DIR}/pending/"*.json; do
|
||||
for action_file in "${OPS_VAULT_DIR}/pending/"*.json; do
|
||||
[ -f "$action_file" ] || continue
|
||||
|
||||
ACTION_STATUS=$(jq -r '.status // ""' < "$action_file" 2>/dev/null)
|
||||
|
|
@ -142,7 +143,7 @@ for action_file in "${VAULT_DIR}/pending/"*.json; do
|
|||
if [ "$AGE_SECS" -gt "$TIMEOUT_SECS" ]; then
|
||||
AGE_HOURS=$((AGE_SECS / 3600))
|
||||
log "timeout: $ACTION_ID escalated ${AGE_HOURS}h ago with no reply — auto-rejecting"
|
||||
bash "${VAULT_DIR}/vault-reject.sh" "$ACTION_ID" "timeout (${AGE_HOURS}h, no human reply)" >> "$LOGFILE" 2>&1 || true
|
||||
bash "${VAULT_SCRIPT_DIR}/vault-reject.sh" "$ACTION_ID" "timeout (${AGE_HOURS}h, no human reply)" >> "$LOGFILE" 2>&1 || true
|
||||
fi
|
||||
done
|
||||
|
||||
|
|
@ -154,7 +155,7 @@ status "phase 3: processing pending actions"
|
|||
PENDING_COUNT=0
|
||||
PENDING_SUMMARY=""
|
||||
|
||||
for action_file in "${VAULT_DIR}/pending/"*.json; do
|
||||
for action_file in "${OPS_VAULT_DIR}/pending/"*.json; do
|
||||
[ -f "$action_file" ] || continue
|
||||
|
||||
ACTION_STATUS=$(jq -r '.status // ""' < "$action_file" 2>/dev/null)
|
||||
|
|
@ -181,7 +182,7 @@ if [ "$PENDING_COUNT" -gt 0 ]; then
|
|||
log "found $PENDING_COUNT pending action(s), invoking vault-agent"
|
||||
status "invoking vault-agent for $PENDING_COUNT action(s)"
|
||||
|
||||
bash "${VAULT_DIR}/vault-agent.sh" >> "$LOGFILE" 2>&1 || {
|
||||
bash "${VAULT_SCRIPT_DIR}/vault-agent.sh" >> "$LOGFILE" 2>&1 || {
|
||||
log "ERROR: vault-agent failed"
|
||||
}
|
||||
fi
|
||||
|
|
@ -193,12 +194,12 @@ status "phase 4: processing pending procurement requests"
|
|||
|
||||
PROCURE_COUNT=0
|
||||
|
||||
for req_file in "${VAULT_DIR}/pending/"*.md; do
|
||||
for req_file in "${OPS_VAULT_DIR}/pending/"*.md; do
|
||||
[ -f "$req_file" ] || continue
|
||||
REQ_ID=$(basename "$req_file" .md)
|
||||
|
||||
# Check if already notified (marker file)
|
||||
if [ -f "${VAULT_DIR}/.locks/${REQ_ID}.notified" ]; then
|
||||
if [ -f "${LOCKS_DIR}/${REQ_ID}.notified" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
|
|
@ -215,8 +216,8 @@ for req_file in "${VAULT_DIR}/pending/"*.md; do
|
|||
log "new procurement request: $REQ_ID — $REQ_TITLE"
|
||||
|
||||
# Mark as notified so we don't re-send
|
||||
mkdir -p "${VAULT_DIR}/.locks"
|
||||
touch "${VAULT_DIR}/.locks/${REQ_ID}.notified"
|
||||
mkdir -p "${LOCKS_DIR}"
|
||||
touch "${LOCKS_DIR}/${REQ_ID}.notified"
|
||||
|
||||
unlock_action "$REQ_ID"
|
||||
done
|
||||
|
|
@ -239,7 +240,7 @@ if [ -n "${FORGE_REPO:-}" ] && [ -n "${FORGE_TOKEN:-}" ]; then
|
|||
ISSUE_NUM=$(printf '%s' "$ACTION_ISSUES" | jq -r ".[$idx].number")
|
||||
|
||||
# Skip if already processed
|
||||
if [ -f "${VAULT_DIR}/.locks/issue-${ISSUE_NUM}.vault-fired" ]; then
|
||||
if [ -f "${LOCKS_DIR}/issue-${ISSUE_NUM}.vault-fired" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
|
|
@ -272,21 +273,21 @@ if [ -n "${FORGE_REPO:-}" ] && [ -n "${FORGE_TOKEN:-}" ]; then
|
|||
fi
|
||||
|
||||
# Skip if this action already exists in any stage
|
||||
if [ -f "${VAULT_DIR}/approved/${ACTION_ID}.json" ] || \
|
||||
[ -f "${VAULT_DIR}/fired/${ACTION_ID}.json" ] || \
|
||||
[ -f "${VAULT_DIR}/rejected/${ACTION_ID}.json" ]; then
|
||||
if [ -f "${OPS_VAULT_DIR}/approved/${ACTION_ID}.json" ] || \
|
||||
[ -f "${OPS_VAULT_DIR}/fired/${ACTION_ID}.json" ] || \
|
||||
[ -f "${OPS_VAULT_DIR}/rejected/${ACTION_ID}.json" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
log "vault-bot authorized action on issue #${ISSUE_NUM}: ${ACTION_ID}"
|
||||
printf '%s' "$ACTION_JSON" | jq '.status = "approved"' > "${VAULT_DIR}/approved/${ACTION_ID}.json"
|
||||
printf '%s' "$ACTION_JSON" | jq '.status = "approved"' > "${OPS_VAULT_DIR}/approved/${ACTION_ID}.json"
|
||||
COMMENT_COUNT=$((COMMENT_COUNT + 1))
|
||||
|
||||
# Fire the action
|
||||
if bash "${VAULT_DIR}/vault-fire.sh" "$ACTION_ID" >> "$LOGFILE" 2>&1; then
|
||||
if bash "${VAULT_SCRIPT_DIR}/vault-fire.sh" "$ACTION_ID" >> "$LOGFILE" 2>&1; then
|
||||
log "fired ${ACTION_ID} from issue #${ISSUE_NUM}"
|
||||
# Mark issue as processed
|
||||
touch "${VAULT_DIR}/.locks/issue-${ISSUE_NUM}.vault-fired"
|
||||
touch "${LOCKS_DIR}/issue-${ISSUE_NUM}.vault-fired"
|
||||
else
|
||||
log "ERROR: fire failed for ${ACTION_ID} from issue #${ISSUE_NUM}"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -8,8 +8,9 @@ set -euo pipefail
|
|||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
source "${SCRIPT_DIR}/vault-env.sh"
|
||||
|
||||
VAULT_DIR="${FACTORY_ROOT}/vault"
|
||||
LOGFILE="${VAULT_DIR}/vault.log"
|
||||
OPS_VAULT_DIR="${OPS_REPO_ROOT}/vault"
|
||||
LOGFILE="${FACTORY_ROOT}/vault/vault.log"
|
||||
LOCKS_DIR="${FACTORY_ROOT}/vault/.locks"
|
||||
|
||||
log() {
|
||||
printf '[%s] vault-reject: %s\n' "$(date -u '+%Y-%m-%d %H:%M:%S UTC')" "$*" >> "$LOGFILE"
|
||||
|
|
@ -20,10 +21,10 @@ REASON="${2:-unspecified}"
|
|||
|
||||
# Find the action file
|
||||
ACTION_FILE=""
|
||||
if [ -f "${VAULT_DIR}/pending/${ACTION_ID}.json" ]; then
|
||||
ACTION_FILE="${VAULT_DIR}/pending/${ACTION_ID}.json"
|
||||
elif [ -f "${VAULT_DIR}/approved/${ACTION_ID}.json" ]; then
|
||||
ACTION_FILE="${VAULT_DIR}/approved/${ACTION_ID}.json"
|
||||
if [ -f "${OPS_VAULT_DIR}/pending/${ACTION_ID}.json" ]; then
|
||||
ACTION_FILE="${OPS_VAULT_DIR}/pending/${ACTION_ID}.json"
|
||||
elif [ -f "${OPS_VAULT_DIR}/approved/${ACTION_ID}.json" ]; then
|
||||
ACTION_FILE="${OPS_VAULT_DIR}/approved/${ACTION_ID}.json"
|
||||
else
|
||||
log "ERROR: action $ACTION_ID not found in pending/ or approved/"
|
||||
exit 1
|
||||
|
|
@ -33,10 +34,10 @@ fi
|
|||
TMP=$(mktemp)
|
||||
jq --arg reason "$REASON" --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
||||
'.status = "rejected" | .rejected_at = $ts | .reject_reason = $reason' \
|
||||
"$ACTION_FILE" > "$TMP" && mv "$TMP" "${VAULT_DIR}/rejected/${ACTION_ID}.json"
|
||||
"$ACTION_FILE" > "$TMP" && mv "$TMP" "${OPS_VAULT_DIR}/rejected/${ACTION_ID}.json"
|
||||
rm -f "$ACTION_FILE"
|
||||
|
||||
# Clean up lock if present
|
||||
rm -f "${VAULT_DIR}/.locks/${ACTION_ID}.lock"
|
||||
rm -f "${LOCKS_DIR}/${ACTION_ID}.lock"
|
||||
|
||||
log "$ACTION_ID: rejected — $REASON"
|
||||
|
|
|
|||
|
|
@ -12,8 +12,9 @@
|
|||
|
||||
set -euo pipefail
|
||||
|
||||
VAULT_DIR="${DISINTO_VAULT_DIR:-/home/agent/disinto/vault}"
|
||||
LOGFILE="${VAULT_DIR}/vault.log"
|
||||
VAULT_SCRIPT_DIR="${DISINTO_VAULT_DIR:-/home/agent/disinto/vault}"
|
||||
OPS_VAULT_DIR="${DISINTO_OPS_VAULT_DIR:-${VAULT_SCRIPT_DIR}}"
|
||||
LOGFILE="${VAULT_SCRIPT_DIR}/vault.log"
|
||||
ACTION_ID="${1:?Usage: vault-run-action.sh <action-id>}"
|
||||
|
||||
log() {
|
||||
|
|
@ -22,7 +23,7 @@ log() {
|
|||
}
|
||||
|
||||
# Find action file in approved/
|
||||
ACTION_FILE="${VAULT_DIR}/approved/${ACTION_ID}.json"
|
||||
ACTION_FILE="${OPS_VAULT_DIR}/approved/${ACTION_ID}.json"
|
||||
if [ ! -f "$ACTION_FILE" ]; then
|
||||
log "ERROR: action file not found: ${ACTION_FILE}"
|
||||
echo "ERROR: action file not found: ${ACTION_FILE}" >&2
|
||||
|
|
@ -118,7 +119,7 @@ case "$ACTION_TYPE" in
|
|||
;;
|
||||
|
||||
blog-post|social-post|email-blast|pricing-change|dns-change|stripe-charge)
|
||||
HANDLER="${VAULT_DIR}/handlers/${ACTION_TYPE}.sh"
|
||||
HANDLER="${VAULT_SCRIPT_DIR}/handlers/${ACTION_TYPE}.sh"
|
||||
if [ -x "$HANDLER" ]; then
|
||||
bash "$HANDLER" "$ACTION_ID" "$PAYLOAD" 2>&1 || FIRE_EXIT=$?
|
||||
else
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue