feat(claude): env-vars refactor + stale-claim cron (Phase 2 finalization)
#11: slash-commands и _autonomous_pickup.md теперь читают persistent User-scope env vars напрямую (FORGEJO_TOKEN_<ROLE>) вместо file-based из ~/.claude/secrets/. Один раз: scripts/setup-bot-env.ps1 (запускается user'ом локально, кладёт токены из vault в Windows User registry). После этого окна grab credentials через [System.Environment]::GetEnvironmentVariable() — никаких файлов. Все 5 work-as-*.md обновлены (analyst/backend/frontend/reviewer/qa). #12: stale-claim cleanup — cron каждые 30 минут. - scripts/cleanup-stale-claims.sh — bash, parses Forgejo API + Python для JSON - .forgejo/workflows/stale-claims.yml — cron schedule `*/30 * * * *` Освобождает issues застрявшие в status/wip >4h: - assignees=[] - +status/ready -status/wip - comment "stale claim released, worker likely crashed" Использует secret FORGEJO_BOT_QA_TOKEN (минимум прав = write:issue). Setup: secret нужно завести в Forgejo Actions UI после merge — git.gendsgn.ru/lekss361/gendesign/-/settings/actions/secrets. Refs: PRs #608, #609. Closes TODO с PR #608 (stale-claim cron).
This commit is contained in:
parent
532b8cdda2
commit
297eb29d65
9 changed files with 366 additions and 70 deletions
|
|
@ -15,42 +15,50 @@ created_at: 2026-05-27
|
|||
> **Это критично.** Без правильной настройки git identity → commits будут писаться
|
||||
> под user'ом (lekss361), не под ботом. Audit trail сломается.
|
||||
|
||||
### Шаг 1 — Получить PAT из vault
|
||||
### Шаг 1 — Где живут PAT'ы
|
||||
|
||||
PAT'ы для всех 5 ботов хранятся в **`meta/00_credentials.md`** (vault, sensitive,
|
||||
не в git). Один раз выгрузи их в локальные secrets:
|
||||
PAT'ы хранятся в **двух местах одновременно**:
|
||||
|
||||
1. **Vault** `meta/00_credentials.md` — sensitive backup (read-only reference)
|
||||
2. **Windows User-scope env vars** — production-ready, **persistent**:
|
||||
- `FORGEJO_TOKEN_ANALYST`
|
||||
- `FORGEJO_TOKEN_BACKEND`
|
||||
- `FORGEJO_TOKEN_FRONTEND`
|
||||
- `FORGEJO_TOKEN_REVIEWER`
|
||||
- `FORGEJO_TOKEN_QA`
|
||||
- `FORGEJO_URL_BOTS` = `https://git.gendsgn.ru`
|
||||
- `FORGEJO_REPO_BOTS` = `lekss361/gendesign`
|
||||
|
||||
Setup один раз через PowerShell (см. `scripts/setup-bot-env.ps1`). После этого
|
||||
env vars доступны во **всех** новых shell-сессиях автоматически — никаких
|
||||
`Get-Content` / файлов.
|
||||
|
||||
Проверь что выставлены:
|
||||
|
||||
```powershell
|
||||
# Локация — никогда не коммитится (gitignored через ~/.gitignore_global)
|
||||
New-Item -ItemType Directory -Force ~/.claude/secrets | Out-Null
|
||||
|
||||
# Для каждого бота — скопируй PAT из vault в файл (открой meta/00_credentials.md,
|
||||
# секция "Forgejo bot-users", найди PAT для bot-<role>, вставь сюда):
|
||||
# ~/.claude/secrets/forgejo-bot-analyst.token
|
||||
# ~/.claude/secrets/forgejo-bot-backend.token
|
||||
# ~/.claude/secrets/forgejo-bot-frontend.token
|
||||
# ~/.claude/secrets/forgejo-bot-reviewer.token
|
||||
# ~/.claude/secrets/forgejo-bot-qa.token
|
||||
#
|
||||
# Пример (с placeholder — НЕ commit'ить настоящий token):
|
||||
# "<PAT-FROM-VAULT>" | Set-Content ~/.claude/secrets/forgejo-bot-<role>.token -NoNewline
|
||||
[System.Environment]::GetEnvironmentVariable("FORGEJO_TOKEN_BACKEND", "User") | Out-Null
|
||||
if (-not $?) { Write-Error "❌ Запусти setup-bot-env.ps1 сначала" }
|
||||
```
|
||||
|
||||
⚠️ Проверь что `~/.claude/secrets/` есть в `~/.gitignore_global` (или эквивалентном).
|
||||
|
||||
### Шаг 2 — Env vars + git identity (per окно)
|
||||
|
||||
Замени `<role>` на свою роль (`analyst`/`backend`/`frontend`/`reviewer`/`qa`):
|
||||
Замени `<ROLE>` на свою роль (`ANALYST`/`BACKEND`/`FRONTEND`/`REVIEWER`/`QA` — UPPER-case):
|
||||
|
||||
```powershell
|
||||
$ROLE = "backend" # ← ИЗМЕНИ ПЕРЕД ЗАПУСКОМ
|
||||
$BOT = "bot-$ROLE"
|
||||
$ROLE = "BACKEND" # ← ИЗМЕНИ ПЕРЕД ЗАПУСКОМ (UPPER-case)
|
||||
$BOT = "bot-$($ROLE.ToLower())"
|
||||
|
||||
# Forgejo API credentials
|
||||
$env:FORGEJO_TOKEN = (Get-Content ~/.claude/secrets/forgejo-$BOT.token -Raw).Trim()
|
||||
# Resolve token из persistent User env
|
||||
$env:FORGEJO_TOKEN = [System.Environment]::GetEnvironmentVariable("FORGEJO_TOKEN_$ROLE", "User")
|
||||
$env:BOT_USERNAME = $BOT
|
||||
$env:FORGEJO_URL = "https://git.gendsgn.ru"
|
||||
$env:FORGEJO_REPO = "lekss361/gendesign"
|
||||
$env:FORGEJO_URL = [System.Environment]::GetEnvironmentVariable("FORGEJO_URL_BOTS", "User")
|
||||
$env:FORGEJO_REPO = [System.Environment]::GetEnvironmentVariable("FORGEJO_REPO_BOTS", "User")
|
||||
|
||||
# Sanity: token не пустой
|
||||
if (-not $env:FORGEJO_TOKEN) {
|
||||
Write-Error "❌ FORGEJO_TOKEN_$ROLE не выставлен. Запусти scripts/setup-bot-env.ps1"
|
||||
return
|
||||
}
|
||||
|
||||
# Git identity — КРИТИЧНО, иначе commit author будет user'а (lekss361)
|
||||
$env:GIT_AUTHOR_NAME = $BOT
|
||||
|
|
|
|||
|
|
@ -9,19 +9,28 @@ description: Запустить окно как auto-analyst (декомпози
|
|||
|
||||
## Pre-flight checks (выполни СЕЙЧАС, до /loop)
|
||||
|
||||
1. **Verify env vars set:**
|
||||
- `$env:FORGEJO_TOKEN` (для bot-analyst — см. `meta/00_credentials.md`)
|
||||
- `$env:BOT_USERNAME = "bot-analyst"`
|
||||
- `$env:FORGEJO_URL = "https://git.gendsgn.ru"`
|
||||
- `$env:FORGEJO_REPO = "lekss361/gendesign"`
|
||||
```powershell
|
||||
# 1. Resolve credentials из persistent User env
|
||||
$env:FORGEJO_TOKEN = [System.Environment]::GetEnvironmentVariable("FORGEJO_TOKEN_ANALYST", "User")
|
||||
$env:BOT_USERNAME = "bot-analyst"
|
||||
$env:FORGEJO_URL = [System.Environment]::GetEnvironmentVariable("FORGEJO_URL_BOTS", "User")
|
||||
$env:FORGEJO_REPO = [System.Environment]::GetEnvironmentVariable("FORGEJO_REPO_BOTS", "User")
|
||||
|
||||
2. **Verify identity:**
|
||||
```bash
|
||||
curl -sH "Authorization: token $FORGEJO_TOKEN" "$FORGEJO_URL/api/v1/user" | jq .login
|
||||
# Должен вернуть "bot-analyst"
|
||||
```
|
||||
if (-not $env:FORGEJO_TOKEN) {
|
||||
Write-Error "❌ FORGEJO_TOKEN_ANALYST не выставлен. Запусти scripts/setup-bot-env.ps1"
|
||||
return
|
||||
}
|
||||
|
||||
3. **Если pre-flight FAILS** — НЕ запускай /loop. Сообщи user'у что нужно настроить.
|
||||
# 2. Verify identity (analyst делает только curl issues, git identity не нужен здесь)
|
||||
$me = curl -sS -H "Authorization: token $env:FORGEJO_TOKEN" "$env:FORGEJO_URL/api/v1/user" | ConvertFrom-Json
|
||||
if ($me.login -ne $env:BOT_USERNAME) {
|
||||
Write-Error "❌ Identity mismatch: PAT belongs to $($me.login), expected $env:BOT_USERNAME"
|
||||
return
|
||||
}
|
||||
Write-Host "✓ PAT belongs to $($me.login) — analyst persona ready"
|
||||
```
|
||||
|
||||
**Если pre-flight FAILS** — НЕ запускай /loop. Обычно — env vars не выставлены, запусти `setup-bot-env.ps1`.
|
||||
|
||||
## Behavior contract
|
||||
|
||||
|
|
|
|||
|
|
@ -9,35 +9,40 @@ description: Запустить окно как auto-backend (pickup scope/backe
|
|||
|
||||
## Pre-flight checks (выполни СЕЙЧАС, до /loop)
|
||||
|
||||
1. **Env vars (см. `.claude/agents/_autonomous_pickup.md` Шаги 1-2):**
|
||||
```powershell
|
||||
$env:FORGEJO_TOKEN = Get-Content ~/.claude/secrets/forgejo-bot-backend.token -Raw
|
||||
$env:BOT_USERNAME = "bot-backend"
|
||||
$env:FORGEJO_URL = "https://git.gendsgn.ru"
|
||||
$env:FORGEJO_REPO = "lekss361/gendesign"
|
||||
```
|
||||
```powershell
|
||||
# 1. Resolve credentials из persistent User env (выставлены setup-bot-env.ps1)
|
||||
$env:FORGEJO_TOKEN = [System.Environment]::GetEnvironmentVariable("FORGEJO_TOKEN_BACKEND", "User")
|
||||
$env:BOT_USERNAME = "bot-backend"
|
||||
$env:FORGEJO_URL = [System.Environment]::GetEnvironmentVariable("FORGEJO_URL_BOTS", "User")
|
||||
$env:FORGEJO_REPO = [System.Environment]::GetEnvironmentVariable("FORGEJO_REPO_BOTS", "User")
|
||||
|
||||
2. **Git identity (КРИТИЧНО — иначе commits под lekss361):**
|
||||
```powershell
|
||||
$env:GIT_AUTHOR_NAME = $env:BOT_USERNAME
|
||||
$env:GIT_AUTHOR_EMAIL = "$($env:BOT_USERNAME)@gendsgn.local"
|
||||
$env:GIT_COMMITTER_NAME = $env:GIT_AUTHOR_NAME
|
||||
$env:GIT_COMMITTER_EMAIL = $env:GIT_AUTHOR_EMAIL
|
||||
```
|
||||
if (-not $env:FORGEJO_TOKEN) {
|
||||
Write-Error "❌ FORGEJO_TOKEN_BACKEND не выставлен. Запусти scripts/setup-bot-env.ps1"
|
||||
return
|
||||
}
|
||||
|
||||
3. **Bot-remote для push (audit-log под bot):**
|
||||
```powershell
|
||||
git remote remove forgejo-bot 2>$null
|
||||
git remote add forgejo-bot "https://$($env:BOT_USERNAME):$($env:FORGEJO_TOKEN)@git.gendsgn.ru/lekss361/gendesign.git"
|
||||
```
|
||||
# 2. Git identity — КРИТИЧНО, иначе commits под lekss361
|
||||
$env:GIT_AUTHOR_NAME = $env:BOT_USERNAME
|
||||
$env:GIT_AUTHOR_EMAIL = "$($env:BOT_USERNAME)@gendsgn.local"
|
||||
$env:GIT_COMMITTER_NAME = $env:GIT_AUTHOR_NAME
|
||||
$env:GIT_COMMITTER_EMAIL = $env:GIT_AUTHOR_EMAIL
|
||||
|
||||
4. **Verify identity:**
|
||||
```bash
|
||||
curl -sH "Authorization: token $FORGEJO_TOKEN" "$FORGEJO_URL/api/v1/user" | jq .login
|
||||
# Должен вернуть "bot-backend"
|
||||
```
|
||||
# 3. Bot-remote для push (audit log под bot, не lekss361)
|
||||
git remote remove forgejo-bot 2>$null
|
||||
git remote add forgejo-bot "https://$($env:BOT_USERNAME):$($env:FORGEJO_TOKEN)@git.gendsgn.ru/lekss361/gendesign.git"
|
||||
|
||||
5. **Если что-то FAILS** — НЕ запускай /loop. Скажи user'у что не так.
|
||||
# 4. Verify identity
|
||||
$me = curl -sS -H "Authorization: token $env:FORGEJO_TOKEN" "$env:FORGEJO_URL/api/v1/user" | ConvertFrom-Json
|
||||
if ($me.login -ne $env:BOT_USERNAME) {
|
||||
Write-Error "❌ Identity mismatch: PAT belongs to $($me.login), expected $env:BOT_USERNAME"
|
||||
return
|
||||
}
|
||||
Write-Host "✓ PAT belongs to $($me.login)"
|
||||
Write-Host "✓ Commits authored as: $env:GIT_AUTHOR_NAME <$env:GIT_AUTHOR_EMAIL>"
|
||||
Write-Host "✓ Use 'git push forgejo-bot' для push (НЕ forgejo — он lekss361)"
|
||||
```
|
||||
|
||||
**Если что-то FAILS** — НЕ запускай /loop. Скажи user'у (обычно — env vars не выставлены, запусти `setup-bot-env.ps1`).
|
||||
|
||||
## Behavior contract
|
||||
|
||||
|
|
|
|||
|
|
@ -9,9 +9,15 @@ description: Запустить окно как auto-frontend (pickup scope/fron
|
|||
|
||||
## Pre-flight checks
|
||||
|
||||
См. `.claude/commands/work-as-backend.md` для полного pre-flight — идентично, только:
|
||||
- `BOT_USERNAME = "bot-frontend"`
|
||||
- PAT из `~/.claude/secrets/forgejo-bot-frontend.token`
|
||||
Идентично `work-as-backend.md`, только изменить две строки:
|
||||
|
||||
```powershell
|
||||
$env:FORGEJO_TOKEN = [System.Environment]::GetEnvironmentVariable("FORGEJO_TOKEN_FRONTEND", "User")
|
||||
$env:BOT_USERNAME = "bot-frontend"
|
||||
# остальные строки (FORGEJO_URL/REPO, git identity, bot-remote, verify) — без изменений
|
||||
```
|
||||
|
||||
См. полный pre-flight в `.claude/commands/work-as-backend.md`.
|
||||
|
||||
## Behavior contract
|
||||
|
||||
|
|
|
|||
|
|
@ -9,9 +9,13 @@ description: Запустить окно как auto-qa-tester (Playwright smoke
|
|||
|
||||
## Pre-flight checks
|
||||
|
||||
Идентично `work-as-backend.md`, только:
|
||||
- `BOT_USERNAME = "bot-qa"`
|
||||
- PAT из `~/.claude/secrets/forgejo-bot-qa.token`
|
||||
Идентично `work-as-backend.md`, только изменить две строки:
|
||||
|
||||
```powershell
|
||||
$env:FORGEJO_TOKEN = [System.Environment]::GetEnvironmentVariable("FORGEJO_TOKEN_QA", "User")
|
||||
$env:BOT_USERNAME = "bot-qa"
|
||||
# остальное — см. work-as-backend.md
|
||||
```
|
||||
|
||||
**Дополнительно** — этот bot использует Playwright MCP, проверь что доступен:
|
||||
|
||||
|
|
|
|||
|
|
@ -9,9 +9,13 @@ description: Запустить окно как auto-code-reviewer (review + mer
|
|||
|
||||
## Pre-flight checks
|
||||
|
||||
Идентично `work-as-backend.md`, только:
|
||||
- `BOT_USERNAME = "bot-reviewer"`
|
||||
- PAT из `~/.claude/secrets/forgejo-bot-reviewer.token`
|
||||
Идентично `work-as-backend.md`, только изменить две строки:
|
||||
|
||||
```powershell
|
||||
$env:FORGEJO_TOKEN = [System.Environment]::GetEnvironmentVariable("FORGEJO_TOKEN_REVIEWER", "User")
|
||||
$env:BOT_USERNAME = "bot-reviewer"
|
||||
# остальное — см. work-as-backend.md
|
||||
```
|
||||
|
||||
**Дополнительно** — этот bot имеет merge authority, поэтому verify scope более строго:
|
||||
|
||||
|
|
|
|||
35
.forgejo/workflows/stale-claims.yml
Normal file
35
.forgejo/workflows/stale-claims.yml
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# Cleanup stale claims for autonomous multi-agent workflow.
|
||||
#
|
||||
# Запускается каждые 30 минут (cron). Освобождает issues застрявшие в
|
||||
# `status/wip` дольше 4 часов. См. `scripts/cleanup-stale-claims.sh`.
|
||||
#
|
||||
# Secrets:
|
||||
# FORGEJO_BOT_QA_TOKEN — bot-qa PAT (write:issue scope нужен).
|
||||
# Можно использовать любой bot-PAT с write:issue, но bot-qa имеет минимум прав.
|
||||
|
||||
name: cleanup-stale-claims
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Каждые 30 минут UTC
|
||||
- cron: '*/30 * * * *'
|
||||
workflow_dispatch: {} # manual run возможен через UI
|
||||
|
||||
jobs:
|
||||
cleanup:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Run cleanup script
|
||||
env:
|
||||
FORGEJO_TOKEN: ${{ secrets.FORGEJO_BOT_QA_TOKEN }}
|
||||
FORGEJO_URL: https://git.gendsgn.ru
|
||||
FORGEJO_REPO: lekss361/gendesign
|
||||
STALE_HOURS: 4
|
||||
run: |
|
||||
chmod +x scripts/cleanup-stale-claims.sh
|
||||
./scripts/cleanup-stale-claims.sh
|
||||
136
scripts/cleanup-stale-claims.sh
Normal file
136
scripts/cleanup-stale-claims.sh
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
#!/usr/bin/env bash
|
||||
# Cleanup stale claims for autonomous multi-agent workflow.
|
||||
#
|
||||
# Освобождает issues застрявшие в `status/wip` дольше 4 часов:
|
||||
# - убирает assignee
|
||||
# - меняет label status/wip → status/ready
|
||||
#
|
||||
# Why: если worker crashes или window закрывается во время работы, issue
|
||||
# остаётся wip+assigned forever. Без cleanup queue забивается.
|
||||
#
|
||||
# Run: cron каждые 30 минут (см. .forgejo/workflows/stale-claims.yml).
|
||||
#
|
||||
# Env vars (требуются):
|
||||
# FORGEJO_TOKEN — admin или с write:issue scope на repo
|
||||
# FORGEJO_URL — https://git.gendsgn.ru (без /api/v1)
|
||||
# FORGEJO_REPO — lekss361/gendesign
|
||||
# STALE_HOURS — порог в часах (default 4)
|
||||
#
|
||||
# Exit codes:
|
||||
# 0 — success (even if 0 stale found)
|
||||
# 1 — env vars missing / API error
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
: "${FORGEJO_TOKEN:?FORGEJO_TOKEN required}"
|
||||
: "${FORGEJO_URL:?FORGEJO_URL required}"
|
||||
: "${FORGEJO_REPO:?FORGEJO_REPO required}"
|
||||
|
||||
STALE_HOURS="${STALE_HOURS:-4}"
|
||||
STALE_SECS=$((STALE_HOURS * 3600))
|
||||
|
||||
curl_forgejo() {
|
||||
curl -sS -H "Authorization: token $FORGEJO_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
"$FORGEJO_URL/api/v1/$1" "${@:2}"
|
||||
}
|
||||
|
||||
now_utc_epoch() {
|
||||
python3 -c 'import datetime; print(int(datetime.datetime.now(datetime.UTC).timestamp()))'
|
||||
}
|
||||
|
||||
iso_to_epoch() {
|
||||
python3 -c "import datetime; print(int(datetime.datetime.fromisoformat('$1'.replace('Z','+00:00')).timestamp()))"
|
||||
}
|
||||
|
||||
echo "[$(date -Is)] cleanup-stale-claims: scanning status/wip issues (stale threshold: ${STALE_HOURS}h)"
|
||||
|
||||
NOW=$(now_utc_epoch)
|
||||
CUTOFF=$((NOW - STALE_SECS))
|
||||
|
||||
# GET all open issues с label status/wip
|
||||
RESPONSE=$(curl_forgejo "repos/$FORGEJO_REPO/issues?labels=status/wip&state=open&limit=50&page=1")
|
||||
|
||||
# Парсим JSON
|
||||
echo "$RESPONSE" | python3 <<PYEOF
|
||||
import json, os, subprocess, sys
|
||||
|
||||
cutoff = int(os.environ.get("CUTOFF", "0"))
|
||||
data = json.loads(sys.stdin.read())
|
||||
|
||||
if not data:
|
||||
print(f"[OK] 0 wip issues — nothing to do")
|
||||
sys.exit(0)
|
||||
|
||||
released = 0
|
||||
errors = 0
|
||||
|
||||
for issue in data:
|
||||
issue_num = issue["number"]
|
||||
updated_at = issue["updated_at"]
|
||||
title = issue["title"]
|
||||
assignees = [a["login"] for a in (issue.get("assignees") or [])]
|
||||
|
||||
# ISO to epoch
|
||||
import datetime
|
||||
updated_epoch = int(datetime.datetime.fromisoformat(updated_at.replace("Z", "+00:00")).timestamp())
|
||||
age_secs = int(datetime.datetime.now(datetime.UTC).timestamp()) - updated_epoch
|
||||
age_hours = age_secs // 3600
|
||||
|
||||
if updated_epoch >= cutoff:
|
||||
print(f" · #{issue_num} '{title[:50]}' — age {age_hours}h, fresh, skip")
|
||||
continue
|
||||
|
||||
print(f" ⚠ #{issue_num} '{title[:50]}' — age {age_hours}h, STALE")
|
||||
print(f" assignees: {assignees}")
|
||||
print(f" → releasing claim")
|
||||
|
||||
# Build commands
|
||||
repo = os.environ["FORGEJO_REPO"]
|
||||
base = os.environ["FORGEJO_URL"] + "/api/v1"
|
||||
token = os.environ["FORGEJO_TOKEN"]
|
||||
auth = ["-H", f"Authorization: token {token}", "-H", "Content-Type: application/json"]
|
||||
|
||||
try:
|
||||
# 1. Clear assignees
|
||||
subprocess.run(
|
||||
["curl", "-sS", "-X", "PATCH", *auth, "-d", '{"assignees": []}',
|
||||
f"{base}/repos/{repo}/issues/{issue_num}"],
|
||||
check=True, capture_output=True,
|
||||
)
|
||||
|
||||
# 2. Add status/ready label
|
||||
subprocess.run(
|
||||
["curl", "-sS", "-X", "POST", *auth, "-d", '{"labels": ["status/ready"]}',
|
||||
f"{base}/repos/{repo}/issues/{issue_num}/labels"],
|
||||
check=True, capture_output=True,
|
||||
)
|
||||
|
||||
# 3. Remove status/wip label
|
||||
subprocess.run(
|
||||
["curl", "-sS", "-X", "DELETE", *auth,
|
||||
f"{base}/repos/{repo}/issues/{issue_num}/labels/status%2Fwip"],
|
||||
check=True, capture_output=True,
|
||||
)
|
||||
|
||||
# 4. Post comment for trace
|
||||
comment_body = json.dumps({
|
||||
"body": f"⚠️ Stale claim released: issue stuck in status/wip for {age_hours}h "
|
||||
f"(previously assigned to {', '.join(assignees) or 'none'}). "
|
||||
f"Label transition: status/wip → status/ready. Worker likely crashed."
|
||||
})
|
||||
subprocess.run(
|
||||
["curl", "-sS", "-X", "POST", *auth, "-d", comment_body,
|
||||
f"{base}/repos/{repo}/issues/{issue_num}/comments"],
|
||||
check=True, capture_output=True,
|
||||
)
|
||||
|
||||
released += 1
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f" ✗ FAILED: {e.stderr.decode() if e.stderr else 'unknown'}")
|
||||
errors += 1
|
||||
|
||||
print(f"")
|
||||
print(f"[OK] Released {released} stale claim(s), {errors} errors")
|
||||
sys.exit(1 if errors else 0)
|
||||
PYEOF
|
||||
89
scripts/setup-bot-env.ps1
Normal file
89
scripts/setup-bot-env.ps1
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
# setup-bot-env.ps1
|
||||
# Set Windows User-scope env vars для autonomous multi-agent workflow.
|
||||
#
|
||||
# Запускается ОДИН РАЗ на каждой машине пользователя. Не нужен admin.
|
||||
# Effect: 7 User-scope env vars persistent в registry.
|
||||
#
|
||||
# Где взять токены:
|
||||
# 1. Vault meta/00_credentials.md (sensitive, не в git) — секция "Forgejo bot-users"
|
||||
# 2. Или сгенерировать новые: см. там же секция "Rotation / revocation"
|
||||
#
|
||||
# Безопасность:
|
||||
# - Скрипт ожидает чтобы ты ВПИСАЛ tokens сам перед запуском (placeholder ниже)
|
||||
# - НЕ коммить скрипт с реальными токенами!
|
||||
# - После запуска env vars в registry — можешь удалить локальную копию
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────
|
||||
# REPLACE PLACEHOLDERS BELOW WITH ACTUAL TOKENS FROM VAULT
|
||||
# ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
$tokens = @{
|
||||
"FORGEJO_TOKEN_ANALYST" = "REPLACE_ME_FROM_VAULT"
|
||||
"FORGEJO_TOKEN_BACKEND" = "REPLACE_ME_FROM_VAULT"
|
||||
"FORGEJO_TOKEN_FRONTEND" = "REPLACE_ME_FROM_VAULT"
|
||||
"FORGEJO_TOKEN_REVIEWER" = "REPLACE_ME_FROM_VAULT"
|
||||
"FORGEJO_TOKEN_QA" = "REPLACE_ME_FROM_VAULT"
|
||||
"FORGEJO_URL_BOTS" = "https://git.gendsgn.ru"
|
||||
"FORGEJO_REPO_BOTS" = "lekss361/gendesign"
|
||||
}
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────
|
||||
# Validation
|
||||
# ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
$placeholderCount = ($tokens.Values | Where-Object { $_ -eq "REPLACE_ME_FROM_VAULT" }).Count
|
||||
if ($placeholderCount -gt 0) {
|
||||
Write-Error "❌ $placeholderCount placeholders не заменены. Открой vault meta/00_credentials.md, секция 'Forgejo bot-users', и вставь реальные PAT'ы вместо REPLACE_ME_FROM_VAULT."
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────
|
||||
# Apply
|
||||
# ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
Write-Host "=== Setting 7 User-scope env vars (persistent) ===" -ForegroundColor Cyan
|
||||
|
||||
foreach ($k in $tokens.Keys) {
|
||||
[System.Environment]::SetEnvironmentVariable($k, $tokens[$k], "User")
|
||||
Set-Item -Path "env:$k" -Value $tokens[$k]
|
||||
|
||||
if ($k -like "FORGEJO_TOKEN_*") {
|
||||
$suffix = $tokens[$k].Substring($tokens[$k].Length - 4)
|
||||
Write-Host " $k = ****$suffix"
|
||||
} else {
|
||||
Write-Host " $k = $($tokens[$k])"
|
||||
}
|
||||
}
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────
|
||||
# Verify
|
||||
# ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "=== Verify (read back from User scope) ===" -ForegroundColor Cyan
|
||||
|
||||
$allOK = $true
|
||||
foreach ($k in $tokens.Keys) {
|
||||
$stored = [System.Environment]::GetEnvironmentVariable($k, "User")
|
||||
if ($stored -eq $tokens[$k]) {
|
||||
Write-Host " $k`: OK (len=$($stored.Length))"
|
||||
} else {
|
||||
Write-Host " $k`: MISMATCH (stored len=$($stored.Length))" -ForegroundColor Red
|
||||
$allOK = $false
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
if ($allOK) {
|
||||
Write-Host "✓ All 7 env vars set successfully." -ForegroundColor Green
|
||||
Write-Host ""
|
||||
Write-Host "Запуск окна:"
|
||||
Write-Host " claude"
|
||||
Write-Host " # в чате:"
|
||||
Write-Host " /work-as-backend # любая из 5: analyst/backend/frontend/reviewer/qa"
|
||||
Write-Host " # pre-flight выполнится сам через env vars"
|
||||
Write-Host " /loop dynamic # или другой cadence"
|
||||
} else {
|
||||
Write-Host "✗ Some env vars failed to set." -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue