feat(kb): skrypt publish.sh dla kb-site + skill przypominajacy o publikacji
Automatyzuje kroki 2-3 z kb/runbooks/kb-site-deploy.md (generacja, kontrola wyciekow, upload przez helper alpine, smoke test) w jednym fail-closed skrypcie zamiast recznego klikania. Runbook zaktualizowany, zeby referencjonowal skrypt, z zachowaniem wyjasnien co/dlaczego dla kazdej bramki. Dodaje skill kb-publish, ktory przypomina operatorowi o publikacji po zmianach w kb/**/*.md, ale nigdy sam nie uruchamia skryptu (dotyka prod przez SSH). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
04884ed67d
commit
003f83d453
26
.claude/skills/kb-publish/SKILL.md
Normal file
26
.claude/skills/kb-publish/SKILL.md
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
name: kb-publish
|
||||
description: Reminds the operator to publish the KB site after changes to kb/**/*.md. Trigger whenever this session's own edits, or a merge/diff visible in git, touch files under kb/.
|
||||
---
|
||||
|
||||
## What this skill does
|
||||
|
||||
At the end of your response, if this session touched `kb/**/*.md` — either
|
||||
through your own edits or through a merge/diff you observed in git — append
|
||||
exactly one reminder sentence:
|
||||
|
||||
> Zmiany w kb/ — pamiętaj odpalić `bash scripts/kb/publish.sh` żeby opublikować.
|
||||
|
||||
One sentence, no more. Don't repeat it more than once per response, and don't
|
||||
add it if nothing under `kb/` changed.
|
||||
|
||||
## What this skill does NOT do
|
||||
|
||||
**Never run `scripts/kb/publish.sh` yourself**, under any circumstances, even
|
||||
if the operator's task prompt says to deploy, publish, or calibrate. The
|
||||
script SSHes into PIHA and overwrites the live `kb-site` content volume on
|
||||
production — that is out of scope for a worktree agent and requires an
|
||||
explicit, direct instruction from the operator in the current turn.
|
||||
|
||||
If the operator explicitly asks you to run it, that instruction stands on its
|
||||
own — this skill only governs the passive reminder, not that request.
|
||||
|
|
@ -61,19 +61,27 @@ docker volume ls | grep kb-site # kb-site_kb-site_content
|
|||
|
||||
---
|
||||
|
||||
## 2. Generacja treści + kontrola wycieków (SATURN / SOLARIA)
|
||||
## 2-3. Generacja, kontrola wycieków, wgranie treści (SATURN / SOLARIA)
|
||||
|
||||
Generujemy w checkoucie repo, na węźle z którego pracujesz — nie na PIHA.
|
||||
Kroki 2-4 (generacja → `--check` → upload przez helper alpine → smoke test)
|
||||
robi jeden skrypt:
|
||||
|
||||
```bash
|
||||
set -a; . /opt/homelab/config/kb-site/.env; set +a # ACCESS_TOKEN
|
||||
test -n "$ACCESS_TOKEN" || echo "BRAK TOKENU — nie publikuj tego builda"
|
||||
|
||||
python3 scripts/kb/gen_pages.py --base-url https://kb-e2a24af3.okit.pl
|
||||
python3 scripts/kb/gen_pages.py --check
|
||||
echo $? # 0 = czysto, 1 = trafienia
|
||||
bash scripts/kb/publish.sh
|
||||
```
|
||||
|
||||
Zobacz kod (`scripts/kb/publish.sh`) dla dokładnej kolejności komend. Poniżej
|
||||
zostaje wyjaśnienie **co** skrypt robi i **dlaczego** — nie trzeba już klikać
|
||||
kroków ręcznie, ale bramki i uzasadnienia poniżej wciąż obowiązują.
|
||||
|
||||
Uruchamiamy w checkoucie repo, na węźle z którego pracujesz — nie na PIHA.
|
||||
Docelowy host PIHA jest parametrem na górze skryptu (zmienna `PIHA_HOST`,
|
||||
domyślnie `piha.local`).
|
||||
|
||||
Skrypt jest fail-closed na każdym etapie: brak/pusty `ACCESS_TOKEN`, `--check`
|
||||
z exit 1, albo 0 wystąpień `key=` w wygenerowanym `index.html` — każde z tych
|
||||
zatrzymuje publikację przed dotknięciem PIHA.
|
||||
|
||||
### Token w linkach
|
||||
|
||||
Generator czyta `ACCESS_TOKEN` ze środowiska i dopisuje `?key=<token>` do
|
||||
|
|
@ -127,39 +135,21 @@ Każdy kolejny wyjątek podlega tej samej regule: najpierw próba wyczyszczenia
|
|||
źródła, wpis do whitelisty dopiero jako świadoma decyzja, zawsze z komentarzem
|
||||
i datą.
|
||||
|
||||
---
|
||||
|
||||
## 3. Wgranie treści do wolumenu (helper alpine)
|
||||
### Wgranie treści do wolumenu (helper alpine)
|
||||
|
||||
Wzorzec z `services/narty27/README.md`, rozszerzony z jednego pliku na drzewo:
|
||||
`docker cp` nie zapisze do montowania `:ro`, więc treść wjeżdża jednorazowym
|
||||
kontenerem pomocniczym, który montuje wolumen zapisywalnie.
|
||||
|
||||
```bash
|
||||
# 1. spakuj build (na węźle generującym)
|
||||
tar -czf /tmp/kb-site.tgz -C build/kb-site .
|
||||
|
||||
# 2. wyślij na PIHA
|
||||
scp /tmp/kb-site.tgz piha:/tmp/kb-site.tgz
|
||||
|
||||
# 3. podmień zawartość wolumenu przez helper
|
||||
ssh piha 'docker run --rm \
|
||||
-v kb-site_kb-site_content:/content \
|
||||
-v /tmp:/src:ro \
|
||||
alpine sh -c "rm -rf /content/* /content/.[!.]* 2>/dev/null; \
|
||||
tar -xzf /src/kb-site.tgz -C /content"'
|
||||
|
||||
# 4. posprzątaj staging
|
||||
rm /tmp/kb-site.tgz
|
||||
ssh piha 'rm /tmp/kb-site.tgz'
|
||||
```
|
||||
kontenerem pomocniczym, który montuje wolumen zapisywalnie. `publish.sh` robi
|
||||
dokładnie to: pakuje `build/kb-site` do tarballa, `scp` na PIHA, i podmienia
|
||||
wolumen przez ten sam wzorzec helpera co niżej.
|
||||
|
||||
`rm -rf /content/*` przed rozpakowaniem jest **obowiązkowe**: bez tego strona
|
||||
dokumentu przełączonego z `public` na `private` zostałaby w wolumenie i dalej
|
||||
serwowała treść, której już nie publikujemy. Publikacja jest podmianą całości,
|
||||
nie dogrywaniem plików.
|
||||
|
||||
Weryfikacja z PIHA:
|
||||
Skrypt kończy smoke testem (`ssh $PIHA_HOST curl -s localhost:8250`) — jeśli
|
||||
chcesz dodatkowej weryfikacji z samej PIHA:
|
||||
|
||||
```bash
|
||||
services/kb-site/healthcheck.sh
|
||||
|
|
@ -267,23 +257,13 @@ pilnuje pola `visibility`, ale to człowiek decyduje, co dostaje `public`.
|
|||
Po każdej zmianie w `kb/**/*.md`, która dotyczy dokumentów `public`:
|
||||
|
||||
```bash
|
||||
set -a; . /opt/homelab/config/kb-site/.env; set +a # ACCESS_TOKEN
|
||||
|
||||
test -n "$ACCESS_TOKEN" \
|
||||
&& python3 scripts/kb/gen_pages.py --base-url https://kb-e2a24af3.okit.pl \
|
||||
&& python3 scripts/kb/gen_pages.py --check \
|
||||
&& grep -q 'key=' build/kb-site/index.html \
|
||||
&& tar -czf /tmp/kb-site.tgz -C build/kb-site . \
|
||||
&& scp /tmp/kb-site.tgz piha:/tmp/kb-site.tgz \
|
||||
&& ssh piha 'docker run --rm -v kb-site_kb-site_content:/content -v /tmp:/src:ro \
|
||||
alpine sh -c "rm -rf /content/* /content/.[!.]* 2>/dev/null; tar -xzf /src/kb-site.tgz -C /content"' \
|
||||
&& ssh piha 'rm /tmp/kb-site.tgz' \
|
||||
&& rm /tmp/kb-site.tgz
|
||||
bash scripts/kb/publish.sh
|
||||
```
|
||||
|
||||
Łańcuch na `&&` jest celowy — każde ogniwo zatrzymuje publikację: pusty
|
||||
`ACCESS_TOKEN` (build byłby nieklikalny), `--check` z exit 1 (wyciek), brak
|
||||
`key=` w spisie (token był pusty albo nie dojechał do generatora).
|
||||
To ten sam skrypt co w krokach 2-3 — rutynowa aktualizacja to po prostu
|
||||
ponowne uruchomienie całej publikacji. Bramki (`ACCESS_TOKEN` pusty, `--check`
|
||||
exit 1, brak `key=` w spisie) zatrzymują ją przed dotknięciem PIHA przy każdym
|
||||
uruchomieniu, nie tylko przy pierwszym.
|
||||
|
||||
Kontener nie wymaga restartu — nginx czyta wolumen na bieżąco.
|
||||
|
||||
|
|
|
|||
73
scripts/kb/publish.sh
Executable file
73
scripts/kb/publish.sh
Executable file
|
|
@ -0,0 +1,73 @@
|
|||
#!/usr/bin/env bash
|
||||
# scripts/kb/publish.sh — publikacja kb-site (kroki 2-4 z kb/runbooks/kb-site-deploy.md)
|
||||
#
|
||||
# Uruchamiane z węzła generującego (SATURN/SOLARIA), nie z PIHA.
|
||||
# Kolejność bramek: token -> generacja -> --check -> sanity linków -> upload -> smoke test.
|
||||
# Każda bramka zatrzymuje publikację przy porażce — patrz runbook, sekcja 2-3.
|
||||
#
|
||||
# Usage: scripts/kb/publish.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
PIHA_HOST="${PIHA_HOST:-piha.local}"
|
||||
BASE_URL="https://kb-e2a24af3.okit.pl"
|
||||
TOKEN_FILE="/opt/homelab/config/kb-site/.env"
|
||||
BUILD_DIR="$REPO_ROOT/build/kb-site"
|
||||
TARBALL="/tmp/kb-site.tgz"
|
||||
|
||||
log() {
|
||||
echo "[kb-publish] $*"
|
||||
}
|
||||
|
||||
fail() {
|
||||
echo "[kb-publish] BLAD: $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
log "krok 1/6: wczytanie ACCESS_TOKEN z $TOKEN_FILE"
|
||||
[[ -f "$TOKEN_FILE" ]] || fail "brak pliku $TOKEN_FILE — patrz kb/runbooks/kb-site-deploy.md sekcja 0"
|
||||
|
||||
set -a
|
||||
# shellcheck disable=SC1090
|
||||
. "$TOKEN_FILE"
|
||||
set +a
|
||||
|
||||
[[ -n "${ACCESS_TOKEN:-}" ]] || fail "ACCESS_TOKEN pusty w $TOKEN_FILE — nie publikuje bez tokenu"
|
||||
|
||||
log "krok 2/6: generacja treści (gen_pages.py --base-url $BASE_URL)"
|
||||
cd "$REPO_ROOT"
|
||||
python3 scripts/kb/gen_pages.py --base-url "$BASE_URL"
|
||||
|
||||
log "krok 3/6: kontrola wyciekow (gen_pages.py --check)"
|
||||
if ! python3 scripts/kb/gen_pages.py --check; then
|
||||
fail "--check wykryl wyciek — publikacja WSTRZYMANA, popraw zrodlo i uruchom ponownie"
|
||||
fi
|
||||
|
||||
log "krok 4/6: sanity check — token w linkach index.html"
|
||||
KEY_COUNT=$(grep -c 'key=' "$BUILD_DIR/index.html" || true)
|
||||
[[ "$KEY_COUNT" -gt 0 ]] || fail "0 wystapien 'key=' w $BUILD_DIR/index.html — token nie trafil do linkow"
|
||||
log " -> $KEY_COUNT linkow z tokenem"
|
||||
|
||||
log "krok 5/6: pakowanie i wgranie na $PIHA_HOST"
|
||||
tar -czf "$TARBALL" -C "$BUILD_DIR" .
|
||||
log " -> scp $TARBALL do $PIHA_HOST:/tmp/kb-site.tgz"
|
||||
scp "$TARBALL" "$PIHA_HOST:/tmp/kb-site.tgz"
|
||||
|
||||
log " -> helper alpine: podmiana wolumenu kb-site_kb-site_content"
|
||||
ssh "$PIHA_HOST" 'docker run --rm \
|
||||
-v kb-site_kb-site_content:/content \
|
||||
-v /tmp:/src:ro \
|
||||
alpine sh -c "rm -rf /content/* /content/.[!.]* 2>/dev/null; \
|
||||
tar -xzf /src/kb-site.tgz -C /content"'
|
||||
|
||||
log " -> sprzatanie /tmp (lokalnie i na $PIHA_HOST)"
|
||||
rm -f "$TARBALL"
|
||||
ssh "$PIHA_HOST" 'rm -f /tmp/kb-site.tgz'
|
||||
|
||||
log "krok 6/6: smoke test na $PIHA_HOST:8250"
|
||||
SMOKE_OUTPUT=$(ssh "$PIHA_HOST" 'curl -s localhost:8250' | head -c 300)
|
||||
log " -> odpowiedz (pierwsze 300 znakow):"
|
||||
echo "$SMOKE_OUTPUT"
|
||||
|
||||
log "publikacja zakonczona."
|
||||
Loading…
Reference in a new issue